JSON_REPLACE
La función JSON_REPLACE modifica valores que ya existen dentro de un documento JSON. Si la ruta especificada no existe en el documento, JSON_REPLACE la ignora silenciosamente sin crear la clave nueva. Este comportamiento la convierte en la opción segura para actualizar datos existentes sin riesgo de agregar claves no deseadas por error.
Sintaxis
JSON_REPLACE(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 nuevo valor para esa ruta. Puedes especificar múltiples pares para reemplazar varios valores a la vez. La función devuelve el documento modificado con los reemplazos aplicados.
Comportamiento básico
La regla fundamental de JSON_REPLACE es que solo modifica claves existentes y nunca crea nuevas:
SELECT JSON_REPLACE(
'{"nombre": "Auriculares BT", "precio": 79.99, "stock": 50}',
'$.precio', 69.99,
'$.color', 'Negro'
) AS resultado;| resultado |
|---|
| {"nombre": "Auriculares BT", "precio": 69.99, "stock": 50} |
La clave precio existía, así que su valor cambió de 79.99 a 69.99. La clave color no existía, así que la operación se ignoró sin generar error. El documento resultante no tiene ninguna clave nueva.
Esta distinción es exactamente lo que diferencia a JSON_REPLACE de JSON_INSERT (que solo agrega nuevas) y JSON_SET (que hace ambas cosas).
Caso práctico: actualizar precios de productos
El caso de uso más habitual de JSON_REPLACE es actualizar valores conocidos dentro de documentos JSON existentes:
CREATE TABLE productos (
id INT AUTO_INCREMENT PRIMARY KEY,
nombre VARCHAR(200) NOT NULL,
datos JSON
);
INSERT INTO productos (nombre, datos) VALUES
('Monitor 4K 27"', '{"marca": "LG", "precio": 349.99, "stock": 45, "activo": true}'),
('Teclado mecánico', '{"marca": "Keychron", "precio": 129.99, "stock": 120, "activo": true}'),
('Ratón gaming', '{"marca": "Logitech", "precio": 79.99, "stock": 200, "activo": true}');Actualizar el precio y el stock del monitor después de una venta:
UPDATE productos
SET datos = JSON_REPLACE(
datos,
'$.precio', 299.99,
'$.stock', 44
)
WHERE id = 1;
SELECT nombre, datos->>'$.precio' AS precio, datos->>'$.stock' AS stock
FROM productos
WHERE id = 1;| nombre | precio | stock |
|---|---|---|
| Monitor 4K 27" | 299.99 | 44 |
Solo los valores especificados cambiaron. La marca y el estado activo permanecen intactos.
Caso práctico: actualizar valores en objetos anidados
JSON_REPLACE funciona con rutas de cualquier profundidad:
INSERT INTO productos (nombre, datos) VALUES
('Portátil ASUS', '{
"marca": "ASUS",
"precio": 1299.00,
"specs": {
"procesador": "Intel i7-13700H",
"ram": "16GB",
"ssd": "512GB"
},
"valoracion": {"media": 4.3, "total": 156}
}');
UPDATE productos
SET datos = JSON_REPLACE(
datos,
'$.specs.ram', '32GB',
'$.specs.ssd', '1TB',
'$.valoracion.media', 4.5,
'$.valoracion.total', 178
)
WHERE nombre = 'Portátil ASUS';
SELECT
datos->>'$.specs.ram' AS ram,
datos->>'$.specs.ssd' AS ssd,
datos->>'$.valoracion.media' AS valoracion,
datos->>'$.valoracion.total' AS resenas
FROM productos
WHERE nombre = 'Portátil ASUS';| ram | ssd | valoracion | resenas |
|---|---|---|---|
| 32GB | 1TB | 4.5 | 178 |
Cada ruta anidada se actualiza independientemente. Si alguna de las rutas no existiera, simplemente se omitiría sin afectar al resto de las actualizaciones.
Caso práctico: desactivar productos masivamente
JSON_REPLACE es útil para actualizaciones masivas donde necesitas cambiar un campo específico en muchas filas:
-- Desactivar todos los productos sin stock
UPDATE productos
SET datos = JSON_REPLACE(datos, '$.activo', FALSE)
WHERE CAST(datos->>'$.stock' AS UNSIGNED) = 0;
-- Aplicar un descuento del 15% a todos los productos activos
UPDATE productos
SET datos = JSON_REPLACE(
datos,
'$.precio',
ROUND(CAST(datos->>'$.precio' AS DECIMAL(10,2)) * 0.85, 2)
)
WHERE datos->>'$.activo' = 'true';
SELECT nombre,
datos->>'$.precio' AS precio_nuevo,
datos->>'$.activo' AS activo
FROM productos;| nombre | precio_nuevo | activo |
|---|---|---|
| Monitor 4K 27" | 254.99 | true |
| Teclado mecánico | 110.49 | true |
| Ratón gaming | 67.99 | true |
| Portátil ASUS | 1104.15 | true |
Observa que el nuevo precio se calcula a partir del valor actual del JSON. Primero se extrae con ->>, se convierte a DECIMAL con CAST, se aplica el descuento y el resultado se reemplaza en el documento.
Reemplazar con diferentes tipos de valores
JSON_REPLACE acepta cualquier tipo de valor, incluyendo objetos y arrays JSON:
UPDATE productos
SET datos = JSON_REPLACE(
datos,
'$.specs', JSON_OBJECT(
'procesador', 'Intel i9-14900HX',
'ram', '64GB DDR5',
'ssd', '2TB NVMe',
'gpu', 'RTX 4080'
)
)
WHERE nombre = 'Portátil ASUS';
SELECT datos->>'$.specs' AS specs
FROM productos
WHERE nombre = 'Portátil ASUS';| specs |
|---|
| {"gpu": "RTX 4080", "ram": "64GB DDR5", "ssd": "2TB NVMe", "procesador": "Intel i9-14900HX"} |
El objeto specs completo fue reemplazado por uno nuevo. Esto reemplaza todo el sub-objeto, no solo las claves individuales. Si quisieras actualizar solo algunas claves del sub-objeto, necesitarías especificar las rutas individuales como hicimos anteriormente.
Reemplazar elementos de arrays
También puedes reemplazar elementos específicos de un array por su índice:
SELECT JSON_REPLACE(
'{"colores": ["rojo", "azul", "verde"]}',
'$.colores[1]', 'amarillo'
) AS resultado;| resultado |
|---|
| {"colores": ["rojo", "amarillo", "verde"]} |
El segundo elemento (índice 1) cambió de "azul" a "amarillo". Si el índice no existe en el array, la operación se ignora:
SELECT JSON_REPLACE(
'{"colores": ["rojo", "azul"]}',
'$.colores[5]', 'inexistente'
) AS resultado;| resultado |
|---|
| {"colores": ["rojo", "azul"]} |
El array solo tiene dos elementos (índices 0 y 1), así que el índice 5 no existe y la operación no tiene efecto.
Manejo de NULL
Cuando el documento JSON original es NULL, JSON_REPLACE devuelve NULL:
SELECT JSON_REPLACE(NULL, '$.clave', 'valor') AS resultado;| resultado |
|---|
| NULL |
Cuando el nuevo valor es NULL, el valor existente se reemplaza por el literal null de JSON:
SELECT JSON_REPLACE(
'{"nombre": "Test", "descripcion": "Original"}',
'$.descripcion', NULL
) AS resultado;| resultado |
|---|
| {"nombre": "Test", "descripcion": null} |
La clave descripcion sigue existiendo en el documento, pero ahora su valor es null. Si quieres eliminar completamente una clave, debes usar JSON_REMOVE en su lugar.
Combinación con otras funciones
JSON_REPLACE se puede encadenar con otras funciones JSON para realizar operaciones complejas en un solo UPDATE. También puedes combinar JSON_REPLACE con JSON_INSERT usando JSON_SET, que es la función que hace ambas cosas a la vez:
-- Actualizar precio y agregar fecha de modificación
UPDATE productos
SET datos = JSON_INSERT(
JSON_REPLACE(datos, '$.precio', 279.99),
'$.ultima_modificacion', CURRENT_TIMESTAMP
)
WHERE id = 1;
SELECT datos->>'$.precio' AS precio,
datos->>'$.ultima_modificacion' AS modificado
FROM productos
WHERE id = 1;| precio | modificado |
|---|---|
| 279.99 | 2024-03-15 10:30:00 |
Primero JSON_REPLACE actualiza el precio (clave existente) y luego JSON_INSERT agrega la fecha de modificación (clave nueva). Aunque funciona, en la práctica es más sencillo usar JSON_SET que combina ambos comportamientos.
En el siguiente artículo veremos JSON_SET que combina inserción y reemplazo.
Escrito por Eduardo Lázaro
