JSON_MERGE_PATCH
La función JSON_MERGE_PATCH combina dos o más documentos JSON siguiendo la semántica definida en el RFC 7396. Su comportamiento fundamental es el de "el último gana": cuando dos documentos tienen la misma clave, el valor del segundo documento reemplaza al del primero. Además, si un valor es null en el segundo documento, la clave correspondiente se elimina del resultado. Esto la convierte en la función ideal para aplicar actualizaciones parciales a documentos JSON, como cuando un usuario modifica algunas configuraciones sin enviar el documento completo.
Sintaxis
JSON_MERGE_PATCH(json_doc, json_doc [, json_doc ...])La función recibe dos o más documentos JSON y los combina de izquierda a derecha. Cada documento posterior actúa como un "parche" que se aplica sobre el resultado acumulado. Las claves que existen en ambos documentos toman el valor del documento más reciente. Las claves que solo existen en uno de los documentos se incluyen tal cual. Las claves cuyo valor es null en el parche se eliminan del resultado.
JSON_MERGE_PATCH(documento_base, parche)
JSON_MERGE_PATCH(documento_base, parche1, parche2)Comportamiento básico
Veamos el caso más simple: combinar dos objetos donde el segundo actualiza algunos campos del primero:
SELECT JSON_MERGE_PATCH(
'{"nombre": "iPhone", "precio": 1299, "color": "negro"}',
'{"precio": 1199, "stock": 50}'
) AS resultado;| resultado |
|---|
| {"color": "negro", "nombre": "iPhone", "precio": 1199, "stock": 50} |
El precio se actualizó de 1299 a 1199 (el valor del segundo documento ganó), la clave stock se añadió porque no existía en el primero, y nombre y color se mantuvieron intactos.
La eliminación de claves mediante null es una de las características más potentes:
SELECT JSON_MERGE_PATCH(
'{"nombre": "iPhone", "precio": 1299, "descuento": 100, "obsoleto": true}',
'{"descuento": null, "obsoleto": null}'
) AS resultado;| resultado |
|---|
| {"nombre": "iPhone", "precio": 1299} |
Las claves descuento y obsoleto fueron eliminadas del resultado porque el parche les asigna null. Este mecanismo es la forma estándar (RFC 7396) de indicar que un campo debe desaparecer.
Cuando se combinan valores que no son objetos, el segundo valor simplemente reemplaza al primero:
SELECT
JSON_MERGE_PATCH('"viejo"', '"nuevo"') AS cadenas,
JSON_MERGE_PATCH('[1, 2]', '[3, 4]') AS arrays,
JSON_MERGE_PATCH('{"a": 1}', '[1, 2]') AS mixto;| cadenas | arrays | mixto |
|---|---|---|
| "nuevo" | [3, 4] | [1, 2] |
Nota especialmente el caso de arrays: JSON_MERGE_PATCH no concatena los arrays, sino que el segundo reemplaza completamente al primero. Si necesitas concatenar arrays, debes usar JSON_MERGE_PRESERVE.
Caso práctico: actualización parcial de configuraciones
El uso más natural de JSON_MERGE_PATCH es aplicar actualizaciones parciales. Imagina una tabla donde cada usuario tiene un perfil de configuración completo, y la aplicación envía solo los campos que han cambiado:
CREATE TABLE usuarios (
id INT PRIMARY KEY AUTO_INCREMENT,
nombre VARCHAR(100),
configuracion JSON
);
INSERT INTO usuarios (nombre, configuracion) VALUES
('María García', '{"tema": "oscuro", "idioma": "es", "notificaciones": {"email": true, "push": true, "sms": false}, "fuente_tamaño": 14}'),
('Carlos López', '{"tema": "claro", "idioma": "es", "notificaciones": {"email": true, "push": false, "sms": false}, "fuente_tamaño": 16}');María quiere cambiar al tema claro y desactivar las notificaciones push. En lugar de enviar todo el documento, la aplicación solo envía el parche:
UPDATE usuarios
SET configuracion = JSON_MERGE_PATCH(
configuracion,
'{"tema": "claro", "notificaciones": {"push": false}}'
)
WHERE nombre = 'María García';Después de la actualización:
SELECT nombre, JSON_PRETTY(configuracion) AS config
FROM usuarios
WHERE nombre = 'María García';| nombre | config |
|---|---|
| María García | {"tema": "claro", "idioma": "es", "notificaciones": {"push": false}, "fuente_tamaño": 14} |
Observa algo crucial: las notificaciones se reemplazaron completamente. El objeto notificaciones del parche ({"push": false}) sustituyó al objeto original ({"email": true, "push": true, "sms": false}), perdiendo las claves email y sms. Este es un comportamiento deliberado de JSON_MERGE_PATCH: los objetos anidados no se fusionan recursivamente por defecto en un solo nivel, sino que el parche reemplaza todo el valor cuando ambos son objetos.
Para hacer una actualización que preserve las otras claves de notificaciones, necesitas ser más específico con la ruta usando JSON_SET, o aplicar el parche a nivel del subobjeto:
UPDATE usuarios
SET configuracion = JSON_SET(
configuracion,
'$.tema', 'claro',
'$.notificaciones.push', CAST('false' AS JSON)
)
WHERE nombre = 'María García';Caso práctico: fusión de datos de producto de múltiples fuentes
Cuando los datos de un producto vienen de varias fuentes (proveedor, almacén, departamento de marketing), puedes fusionarlos aplicando parches sucesivos:
SELECT JSON_MERGE_PATCH(
'{"nombre": "Monitor LG 27UK850", "precio": 549}',
'{"stock": 23, "ubicación": "Almacén B3"}',
'{"descripción": "Monitor 4K HDR profesional", "precio": 499, "oferta": true}'
) AS producto_completo;| producto_completo |
|---|
| {"descripción": "Monitor 4K HDR profesional", "nombre": "Monitor LG 27UK850", "oferta": true, "precio": 499, "stock": 23, "ubicación": "Almacén B3"} |
Los tres documentos se fusionaron de izquierda a derecha. El precio del tercer documento (499) prevaleció sobre el del primero (549). Todas las claves únicas de cada documento se conservaron.
Caso práctico: plantillas con valores por defecto
Otro patrón útil es usar JSON_MERGE_PATCH para aplicar valores por defecto. Defines una plantilla con todos los campos y sus valores por defecto, y luego aplicas los datos del usuario como parche:
SELECT JSON_MERGE_PATCH(
'{"tema": "claro", "idioma": "es", "notificaciones": true, "tamaño_página": 20, "zona_horaria": "UTC"}',
'{"tema": "oscuro", "idioma": "en"}'
) AS config_final;| config_final |
|---|
| {"idioma": "en", "notificaciones": true, "tamaño_página": 20, "tema": "oscuro", "zona_horaria": "UTC"} |
El usuario solo especificó tema e idioma, y todos los demás campos conservaron sus valores por defecto. Esto simplifica enormemente la lógica de la aplicación.
Manejo de NULL
Cuando uno de los argumentos es SQL NULL (no el JSON null), la función devuelve NULL:
SELECT JSON_MERGE_PATCH(NULL, '{"a": 1}') AS resultado;| resultado |
|---|
| NULL |
No confundas el SQL NULL con el JSON null. El JSON null dentro de un parche elimina claves, mientras que el SQL NULL como argumento anula toda la operación.
Cuando el primer documento es un objeto y el segundo es null (JSON), el resultado es null:
SELECT JSON_MERGE_PATCH('{"a": 1}', 'null') AS resultado;| resultado |
|---|
| null |
Combinación con otras funciones
JSON_MERGE_PATCH se combina naturalmente con JSON_OBJECT para construir parches dinámicos:
SELECT JSON_MERGE_PATCH(
'{"nombre": "iPhone", "precio": 1299, "stock": 45}',
JSON_OBJECT('precio', 1199, 'actualizado', NOW())
) AS resultado;| resultado |
|---|
| {"actualizado": "2026-02-14 15:30:00", "nombre": "iPhone", "precio": 1199, "stock": 45} |
También puedes combinarla con JSON_CONTAINS_PATH para verificar si el parche va a afectar ciertas claves antes de aplicarlo, o con JSON_VALID para asegurarte de que ambos documentos son JSON válido antes de la fusión.
La diferencia fundamental con JSON_MERGE_PRESERVE es que JSON_MERGE_PATCH reemplaza valores duplicados, mientras que JSON_MERGE_PRESERVE los combina (convirtiendo valores duplicados en arrays). Si necesitas conservar todos los valores en lugar de reemplazar los anteriores, JSON_MERGE_PRESERVE es la opción adecuada. En el siguiente artículo veremos JSON_MERGE_PRESERVE para combinar documentos preservando todos los valores.
Escrito por Eduardo Lázaro
