JSON_INSERT

La función JSON_INSERT agrega nuevos valores a un documento JSON sin modificar los que ya existen. Si la ruta especificada ya tiene un valor, JSON_INSERT la ignora y deja el valor original intacto. Este comportamiento la convierte en una operación segura para enriquecer documentos JSON con datos adicionales, sin riesgo de sobrescribir información existente por accidente.

Sintaxis

JSON_INSERT(documento_json, ruta, valor [, ruta2, valor2, ...])

El primer argumento es el documento JSON original. Los siguientes argumentos van en pares: una ruta JSON Path y el valor que quieres insertar en esa ruta. Puedes especificar múltiples pares en una sola llamada para agregar varios valores a la vez. La función devuelve el documento modificado; no altera el original directamente.

Comportamiento básico

Veamos la regla fundamental de JSON_INSERT: solo agrega claves nuevas, nunca sobrescribe las existentes:

SELECT JSON_INSERT(
    '{"nombre": "Auriculares BT", "precio": 79.99}',
    '$.color', 'Negro',
    '$.precio', 999.99
) AS resultado;
resultado
{"nombre": "Auriculares BT", "precio": 79.99, "color": "Negro"}

La clave color no existía, así que se agregó con el valor "Negro". La clave precio ya existía con valor 79.99, así que el intento de insertar 999.99 se ignoró silenciosamente. El precio original se mantiene intacto.

Esta distinción entre insertar y reemplazar es exactamente lo que diferencia a JSON_INSERT de JSON_SET (que hace ambas cosas) y JSON_REPLACE (que solo reemplaza).

Caso práctico: enriquecer datos de productos

Imaginemos una tienda online donde los productos se crean inicialmente con información básica y luego se enriquecen gradualmente con más datos:

CREATE TABLE productos (
    id INT AUTO_INCREMENT PRIMARY KEY,
    nombre VARCHAR(200) NOT NULL,
    datos JSON
);
 
INSERT INTO productos (nombre, datos) VALUES
('iPhone 15 Pro', '{"marca": "Apple", "precio": 1299.99, "categoria": "moviles"}'),
('Galaxy S24', '{"marca": "Samsung", "precio": 899.99, "categoria": "moviles"}'),
('MacBook Air M3', '{"marca": "Apple", "precio": 1399.00, "categoria": "portatiles"}');

El equipo de logística quiere agregar información de peso y dimensiones sin alterar los datos existentes:

UPDATE productos
SET datos = JSON_INSERT(
    datos,
    '$.peso_kg', 0.187,
    '$.dimensiones', JSON_OBJECT('alto', 14.66, 'ancho', 7.08, 'grosor', 0.83)
)
WHERE id = 1;
 
SELECT nombre, datos
FROM productos
WHERE id = 1;
nombredatos
iPhone 15 Pro{"marca": "Apple", "precio": 1299.99, "categoria": "moviles", "peso_kg": 0.187, "dimensiones": {"alto": 14.66, "ancho": 7.08, "grosor": 0.83}}

Los campos originales (marca, precio, categoria) permanecen intactos, y se agregaron los nuevos campos peso_kg y dimensiones.

Caso práctico: agregar valores a objetos anidados

JSON_INSERT también funciona con rutas anidadas, siempre que los objetos padres ya existan:

UPDATE productos
SET datos = JSON_INSERT(
    datos,
    '$.dimensiones.unidad', 'cm',
    '$.dimensiones.peso_unidad', 'kg'
)
WHERE id = 1;
 
SELECT datos->>'$.dimensiones' AS dimensiones
FROM productos
WHERE id = 1;
dimensiones
{"alto": 14.66, "ancho": 7.08, "grosor": 0.83, "unidad": "cm", "peso_unidad": "kg"}

La clave unidad se agregó dentro del objeto dimensiones porque ese objeto ya existía. Sin embargo, si intentas insertar en una ruta cuyo objeto padre no existe, la operación no hace nada:

SELECT JSON_INSERT(
    '{"nombre": "Test"}',
    '$.specs.ram', '16GB'
) AS resultado;
resultado
{"nombre": "Test"}

El objeto specs no existe, así que MySQL no puede crear la clave ram dentro de él. Para crear rutas anidadas donde los padres no existen, necesitarás usar JSON_SET o crear primero el objeto padre.

Caso práctico: agregar elementos a arrays

Puedes usar JSON_INSERT para agregar elementos en posiciones específicas de un array, pero con una particularidad importante: solo funciona para posiciones que no existen aún, es decir, posiciones más allá del final del array:

SELECT JSON_INSERT(
    '{"tags": ["electrónica", "ofertas"]}',
    '$.tags[2]', 'nuevo',
    '$.tags[0]', 'intentar_sobrescribir'
) AS resultado;
resultado
{"tags": ["electrónica", "ofertas", "nuevo"]}

El elemento en la posición [2] no existía (el array tenía índices 0 y 1), así que se agregó "nuevo". La posición [0] ya tenía valor, así que se ignoró el intento de inserción. Si necesitas agregar elementos al final de un array sin conocer su longitud, es más práctico usar JSON_ARRAY_APPEND.

Insertar múltiples valores en una sola operación

Una ventaja de JSON_INSERT es que puedes agregar múltiples valores en una sola llamada, lo que es más eficiente que encadenar varias actualizaciones:

UPDATE productos
SET datos = JSON_INSERT(
    datos,
    '$.stock', 150,
    '$.garantia_meses', 24,
    '$.envio_gratuito', TRUE,
    '$.valoracion', JSON_OBJECT('media', 4.7, 'total_resenas', 328)
)
WHERE id = 2;
 
SELECT JSON_PRETTY(datos) AS datos_formateados
FROM productos
WHERE id = 2;
datos_formateados
{ "marca": "Samsung", "precio": 899.99, "categoria": "moviles", "stock": 150, "garantia_meses": 24, "envio_gratuito": true, "valoracion": { "media": 4.7, "total_resenas": 328 } }

En una sola operación hemos añadido un entero, un booleano y un objeto anidado al documento existente.

Manejo de NULL

Cuando el documento JSON original es NULL, JSON_INSERT devuelve NULL:

SELECT JSON_INSERT(NULL, '$.clave', 'valor') AS resultado;
resultado
NULL

Cuando el valor a insertar es NULL, se inserta el literal null de JSON:

SELECT JSON_INSERT(
    '{"nombre": "Test"}',
    '$.descripcion', NULL
) AS resultado;
resultado
{"nombre": "Test", "descripcion": null}

Combinación con otras funciones

JSON_INSERT se usa frecuentemente en combinación con condiciones para agregar datos de forma selectiva. Por ejemplo, puedes usar un CASE para decidir qué valor insertar:

UPDATE productos
SET datos = JSON_INSERT(
    datos,
    '$.rango_precio',
    CASE
        WHEN CAST(datos->>'$.precio' AS DECIMAL(10,2)) > 1000 THEN 'premium'
        WHEN CAST(datos->>'$.precio' AS DECIMAL(10,2)) > 500 THEN 'medio'
        ELSE 'económico'
    END
)
WHERE datos->>'$.rango_precio' IS NULL;
 
SELECT nombre, datos->>'$.rango_precio' AS rango
FROM productos;
nombrerango
iPhone 15 Propremium
Galaxy S24medio
MacBook Air M3premium

Como JSON_INSERT no sobrescribe valores existentes, la cláusula WHERE datos->>'$.rango_precio' IS NULL es técnicamente redundante aquí, pero hace la intención más explícita para quien lea el código.

En el siguiente artículo veremos JSON_REPLACE para reemplazar valores existentes.

Escrito por Eduardo Lázaro