JSON_SET
La función JSON_SET es la más flexible de las tres funciones de modificación de JSON en MySQL. Combina el comportamiento de JSON_INSERT y JSON_REPLACE: si la ruta existe, reemplaza el valor; si no existe, lo crea. Esto la convierte en la opción más práctica cuando no te importa si la clave es nueva o ya tenía un valor previo, que es el caso más habitual en la mayoría de aplicaciones.
Sintaxis
JSON_SET(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 establecer en esa ruta. Puedes especificar múltiples pares para modificar o crear varios valores a la vez. La función devuelve el documento con todos los cambios aplicados.
Comportamiento básico
La clave de JSON_SET es que funciona tanto para claves existentes como para nuevas:
SELECT JSON_SET(
'{"nombre": "Monitor 4K", "precio": 349.99}',
'$.precio', 299.99,
'$.stock', 75
) AS resultado;| resultado |
|---|
| {"nombre": "Monitor 4K", "precio": 299.99, "stock": 75} |
La clave precio existía con valor 349.99, así que se reemplazó por 299.99. La clave stock no existía, así que se creó con valor 75. JSON_SET no distingue entre ambos casos: simplemente establece el valor en la ruta indicada.
Comparación: JSON_INSERT vs JSON_REPLACE vs JSON_SET
Estas tres funciones son idénticas en su sintaxis pero difieren en cuándo actúan. Es fundamental entender la diferencia para elegir la correcta:
SET @doc = '{"nombre": "Teclado", "precio": 89.99}';
SELECT
JSON_INSERT(@doc, '$.precio', 999, '$.stock', 50) AS con_insert,
JSON_REPLACE(@doc, '$.precio', 999, '$.stock', 50) AS con_replace,
JSON_SET(@doc, '$.precio', 999, '$.stock', 50) AS con_set;| con_insert | con_replace | con_set |
|---|---|---|
| {"nombre": "Teclado", "precio": 89.99, "stock": 50} | {"nombre": "Teclado", "precio": 999} | {"nombre": "Teclado", "precio": 999, "stock": 50} |
La tabla resume el comportamiento de cada función:
| Función | Clave existe | Clave no existe |
|---|---|---|
| JSON_INSERT | No modifica | Crea la clave |
| JSON_REPLACE | Modifica el valor | No hace nada |
| JSON_SET | Modifica el valor | Crea la clave |
JSON_SET es la más permisiva: siempre establece el valor, sin importar si la clave existía o no. Por eso es la más usada en la práctica.
Caso práctico: gestionar preferencias de usuario
Un escenario ideal para JSON_SET es la gestión de configuraciones, donde el usuario puede cambiar ajustes existentes o activar nuevos:
CREATE TABLE preferencias (
usuario_id INT PRIMARY KEY,
config JSON NOT NULL DEFAULT ('{}')
);
INSERT INTO preferencias VALUES
(1, '{"tema": "oscuro", "idioma": "es", "notificaciones": true}'),
(2, '{"tema": "claro", "idioma": "en"}'),
(3, '{}');Actualizar la configuración del usuario 1 (cambiar tema existente y agregar nuevo ajuste):
UPDATE preferencias
SET config = JSON_SET(
config,
'$.tema', 'claro',
'$.articulos_por_pagina', 25,
'$.sidebar_colapsada', FALSE
)
WHERE usuario_id = 1;
SELECT config
FROM preferencias
WHERE usuario_id = 1;| config |
|---|
| {"tema": "claro", "idioma": "es", "notificaciones": true, "articulos_por_pagina": 25, "sidebar_colapsada": false} |
El tema cambió de "oscuro" a "claro" (reemplazo), y se añadieron los nuevos ajustes articulos_por_pagina y sidebar_colapsada (inserción). Todo en una sola operación.
Caso práctico: actualizar datos de producto en e-commerce
En una tienda online, las actualizaciones de productos suelen mezclar cambios a datos existentes con información nueva:
CREATE TABLE catalogo (
id INT AUTO_INCREMENT PRIMARY KEY,
nombre VARCHAR(200) NOT NULL,
datos JSON
);
INSERT INTO catalogo (nombre, datos) VALUES
('Samsung Galaxy S24', '{
"marca": "Samsung",
"precio": 899.99,
"stock": 150,
"especificaciones": {
"pantalla": "6.2 pulgadas",
"procesador": "Exynos 2400",
"ram": "8GB"
}
}');-- Actualizar precio, reducir stock, cambiar procesador y agregar información nueva
UPDATE catalogo
SET datos = JSON_SET(
datos,
'$.precio', 849.99,
'$.stock', 148,
'$.especificaciones.procesador', 'Snapdragon 8 Gen 3',
'$.especificaciones.almacenamiento', '256GB',
'$.envio_gratuito', TRUE,
'$.ultima_actualizacion', NOW()
)
WHERE id = 1;
SELECT nombre,
datos->>'$.precio' AS precio,
datos->>'$.stock' AS stock,
datos->>'$.especificaciones.procesador' AS cpu,
datos->>'$.especificaciones.almacenamiento' AS storage,
datos->>'$.envio_gratuito' AS envio_gratis
FROM catalogo
WHERE id = 1;| nombre | precio | stock | cpu | storage | envio_gratis |
|---|---|---|---|---|---|
| Samsung Galaxy S24 | 849.99 | 148 | Snapdragon 8 Gen 3 | 256GB | true |
En una sola sentencia hemos actualizado datos existentes (precio, stock, procesador) y agregado datos nuevos (almacenamiento, envio_gratuito, ultima_actualizacion), incluyendo modificaciones dentro de objetos anidados.
Caso práctico: construir un documento JSON progresivamente
A veces es necesario construir un documento JSON paso a paso, empezando desde un objeto vacío:
INSERT INTO preferencias VALUES (4, '{}');
UPDATE preferencias
SET config = JSON_SET(config, '$.tema', 'sistema')
WHERE usuario_id = 4;
UPDATE preferencias
SET config = JSON_SET(config, '$.idioma', 'es', '$.moneda', 'EUR')
WHERE usuario_id = 4;
UPDATE preferencias
SET config = JSON_SET(
config,
'$.notificaciones', JSON_OBJECT('email', TRUE, 'push', FALSE, 'sms', FALSE)
)
WHERE usuario_id = 4;
SELECT JSON_PRETTY(config) AS config
FROM preferencias
WHERE usuario_id = 4;| config |
|---|
| { "tema": "sistema", "idioma": "es", "moneda": "EUR", "notificaciones": { "email": true, "push": false, "sms": false } } |
Cada UPDATE añade o modifica claves sin afectar a las demás. El documento crece orgánicamente con cada operación.
Establecer valores complejos
JSON_SET acepta cualquier tipo de valor JSON, incluyendo objetos y arrays construidos con las funciones correspondientes:
UPDATE catalogo
SET datos = JSON_SET(
datos,
'$.colores_disponibles', JSON_ARRAY('Negro', 'Violeta', 'Crema', 'Ámbar'),
'$.dimensiones', JSON_OBJECT(
'alto', 147.0,
'ancho', 70.6,
'grosor', 7.6,
'unidad', 'mm'
)
)
WHERE id = 1;
SELECT datos->'$.colores_disponibles' AS colores,
datos->'$.dimensiones' AS dimensiones
FROM catalogo
WHERE id = 1;| colores | dimensiones |
|---|---|
| ["Negro", "Violeta", "Crema", "Ámbar"] | {"alto": 147.0, "ancho": 70.6, "grosor": 7.6, "unidad": "mm"} |
Manejo de NULL
Cuando el documento original es NULL, JSON_SET devuelve NULL:
SELECT JSON_SET(NULL, '$.clave', 'valor') AS resultado;| resultado |
|---|
| NULL |
Si quieres garantizar que siempre hay un documento válido, puedes usar COALESCE:
UPDATE preferencias
SET config = JSON_SET(
COALESCE(config, '{}'),
'$.tema', 'oscuro'
)
WHERE usuario_id = 4;Cuando el valor a establecer es NULL, se escribe como el literal null de JSON:
SELECT JSON_SET('{"a": 1}', '$.b', NULL) AS resultado;| resultado |
|---|
| {"a": 1, "b": null} |
Combinación con otras funciones
JSON_SET funciona especialmente bien con valores calculados a partir del propio documento o de otras fuentes:
-- Aplicar un 10% de descuento y registrar el precio original
UPDATE catalogo
SET datos = JSON_SET(
datos,
'$.precio_original', CAST(datos->>'$.precio' AS DECIMAL(10,2)),
'$.precio', ROUND(CAST(datos->>'$.precio' AS DECIMAL(10,2)) * 0.9, 2),
'$.en_oferta', TRUE
)
WHERE id = 1;
SELECT
datos->>'$.precio_original' AS original,
datos->>'$.precio' AS con_descuento,
datos->>'$.en_oferta' AS oferta
FROM catalogo
WHERE id = 1;| original | con_descuento | oferta |
|---|---|---|
| 849.99 | 764.99 | true |
El precio original se guarda en una clave nueva antes de aplicar el descuento. Como JSON_SET procesa los pares de izquierda a derecha, el valor de $.precio_original se toma del documento antes de que $.precio sea modificado.
En el siguiente artículo veremos JSON_REMOVE para eliminar valores de un documento JSON.
Escrito por Eduardo Lázaro
