JSON_OVERLAPS

La función JSON_OVERLAPS compara dos documentos JSON y devuelve verdadero si comparten al menos un elemento en común. Es una de las funciones más potentes del ecosistema JSON de MySQL porque permite resolver de forma nativa un problema que tradicionalmente requería múltiples joins o subconsultas: detectar intersecciones entre conjuntos de datos. En cualquier aplicación moderna donde los datos se almacenan como arrays JSON (etiquetas, categorías, permisos, horarios, preferencias), JSON_OVERLAPS ofrece una forma declarativa y eficiente de encontrar coincidencias parciales entre documentos.

La diferencia fundamental con JSON_CONTAINS es que JSON_OVERLAPS busca cualquier coincidencia parcial (al menos un elemento compartido), mientras que JSON_CONTAINS verifica que todos los elementos del segundo argumento estén presentes en el primero. Si JSON_CONTAINS es el operador "contiene todo", JSON_OVERLAPS es el operador "tiene algo en común". Esta distinción es crucial cuando diseñas filtros de búsqueda, sistemas de recomendación o validaciones de conflictos.

JSON_OVERLAPS fue introducida en MySQL 8.0.17 y, al igual que MEMBER OF, puede aprovechar los índices multi-valor para acelerar las búsquedas sobre columnas JSON indexadas. Esto la convierte no solo en una herramienta expresiva, sino también en una opción viable para consultas en producción con tablas de gran volumen.

Sintaxis

La sintaxis de JSON_OVERLAPS es sencilla y directa, ya que recibe únicamente dos argumentos.

JSON_OVERLAPS(documento_json1, documento_json2)

La función recibe dos expresiones JSON y devuelve 1 si los documentos tienen al menos un elemento compartido, o 0 si no tienen ninguno. Internamente, el comportamiento de la comparación varía según el tipo de los documentos que recibe como argumentos, y entender estas diferencias es esencial para usarla correctamente.

Cuando ambos argumentos son arrays, la función comprueba si existe al menos un valor presente en ambos. Este es el caso de uso más habitual y el más intuitivo: dos listas que se solapan. Cuando ambos son objetos, la comparación es más estricta de lo que podría parecer a primera vista, ya que no basta con que compartan una clave; deben compartir al menos un par clave-valor idéntico, es decir, tanto la clave como el valor asociado deben coincidir. Cuando uno de los argumentos es un escalar y el otro un array, la función comprueba si ese escalar es miembro del array, comportándose de forma análoga al operador MEMBER OF. Y cuando ambos argumentos son escalares, simplemente compara si son iguales.

Comportamiento básico con arrays

El escenario más frecuente para JSON_OVERLAPS es la comparación de dos arrays. En este caso, la función recorre ambos arrays buscando al menos un valor que esté presente en los dos. Basta con encontrar una sola coincidencia para devolver 1.

SELECT JSON_OVERLAPS('[1, 2, 3]', '[3, 4, 5]') AS resultado;
resultado
1

Ambos arrays contienen el valor 3, por lo que hay solapamiento. La función no indica cuántos elementos coinciden ni cuáles son; solamente informa de que existe al menos una coincidencia. Si necesitas saber exactamente qué elementos comparten dos arrays, tendrás que complementar la consulta con otras funciones como JSON_TABLE para descomponer los arrays y compararlos elemento a elemento.

Cuando no existe ningún elemento en común, el resultado es 0.

SELECT JSON_OVERLAPS('[1, 2, 3]', '[4, 5, 6]') AS resultado;
resultado
0

Un detalle importante es que la comparación de tipos es estricta. MySQL distingue entre un entero y una cadena que representa ese mismo entero, lo que significa que 1 (número) y "1" (cadena) no se consideran iguales.

SELECT JSON_OVERLAPS('[1, 2, 3]', '["1", "2", "3"]') AS resultado;
resultado
0

Los enteros 1, 2, 3 no son iguales a las cadenas "1", "2", "3", por lo que no hay solapamiento. Este comportamiento es consistente con el resto de funciones JSON de MySQL, pero puede resultar sorprendente si vienes de lenguajes con tipado débil como JavaScript o PHP. Asegúrate de que los tipos de datos en tus arrays JSON sean coherentes para evitar falsos negativos en las comparaciones.

Comportamiento con objetos

Cuando ambos argumentos son objetos JSON, JSON_OVERLAPS busca pares clave-valor que coincidan en ambos documentos. No basta con que compartan una clave; el valor asociado a esa clave también debe ser idéntico.

SELECT JSON_OVERLAPS(
    '{"color": "rojo", "talla": "M"}',
    '{"color": "rojo", "peso": 0.5}'
) AS resultado;
resultado
1

Ambos objetos comparten la clave "color" con el valor "rojo", por lo que hay solapamiento. Sin embargo, si la clave coincide pero el valor difiere, la función no detecta solapamiento.

SELECT JSON_OVERLAPS(
    '{"color": "rojo", "talla": "M"}',
    '{"color": "azul", "peso": 0.5}'
) AS resultado;
resultado
0

Aunque ambos tienen la clave "color", los valores son diferentes ("rojo" frente a "azul"), así que la función devuelve 0. Este comportamiento convierte a JSON_OVERLAPS con objetos en una herramienta útil para detectar configuraciones idénticas: si dos objetos de configuración comparten al menos un par clave-valor, puedes inferir que tienen algo en común.

Comportamiento con escalares y tipos mixtos

Cuando uno de los argumentos es un escalar y el otro un array, JSON_OVERLAPS verifica si el escalar existe como elemento dentro del array. Este comportamiento es equivalente al operador MEMBER OF.

SELECT JSON_OVERLAPS('3', '[1, 2, 3, 4]') AS resultado;
resultado
1

El escalar 3 está presente en el array, por lo que el resultado es 1. Si ambos argumentos son escalares, la comparación es una simple igualdad.

SELECT JSON_OVERLAPS('"hola"', '"hola"') AS escalar_igual,
       JSON_OVERLAPS('"hola"', '"mundo"') AS escalar_diferente;
escalar_igualescalar_diferente
10

Caso práctico: sistema de etiquetas de productos

En una tienda online con productos etiquetados, JSON_OVERLAPS permite implementar uno de los patrones más demandados en comercio electrónico: encontrar productos relacionados que compartan características con un producto dado. Este es el corazón de las secciones "También te puede interesar" o "Productos similares" que ves en cualquier e-commerce.

CREATE TABLE productos (
    id INT AUTO_INCREMENT PRIMARY KEY,
    nombre VARCHAR(200),
    precio DECIMAL(10,2),
    etiquetas JSON
);
 
INSERT INTO productos (nombre, precio, etiquetas) VALUES
('iPhone 15 Pro', 1299.99, '["smartphone", "apple", "5g", "premium"]'),
('Samsung Galaxy S24', 899.99, '["smartphone", "samsung", "5g", "android"]'),
('Google Pixel 8', 699.00, '["smartphone", "google", "5g", "android"]'),
('MacBook Air M3', 1399.00, '["portatil", "apple", "ultrabook"]'),
('iPad Air', 699.00, '["tablet", "apple", "educacion"]'),
('Sony WH-1000XM5', 349.00, '["auriculares", "sony", "bluetooth", "premium"]');

Para encontrar productos que compartan al menos una etiqueta con el iPhone 15 Pro, realizamos un self-join excluyendo el propio producto.

SELECT
    p2.nombre,
    p2.precio,
    p2.etiquetas
FROM productos p1
JOIN productos p2 ON p1.id != p2.id
WHERE p1.id = 1
  AND JSON_OVERLAPS(p1.etiquetas, p2.etiquetas);
nombreprecioetiquetas
Samsung Galaxy S24899.99["smartphone", "samsung", "5g", "android"]
Google Pixel 8699.00["smartphone", "google", "5g", "android"]
MacBook Air M31399.00["portatil", "apple", "ultrabook"]
iPad Air699.00["tablet", "apple", "educacion"]
Sony WH-1000XM5349.00["auriculares", "sony", "bluetooth", "premium"]

Todos estos productos comparten al menos una etiqueta con el iPhone. El Samsung y el Pixel comparten "smartphone" y "5g", el MacBook y el iPad comparten "apple", y los auriculares Sony comparten "premium". Este es el patrón básico de un sistema de recomendaciones: "productos relacionados". En una implementación real, podrías además ordenar los resultados por el número de etiquetas compartidas para mostrar primero los productos más relevantes.

Caso práctico: filtrar por múltiples criterios del usuario

Otro uso muy habitual de JSON_OVERLAPS es implementar filtros de búsqueda flexibles. Supongamos que un usuario selecciona varios intereses en un formulario y quieres encontrar productos que coincidan con al menos uno de ellos. En lugar de construir una consulta con múltiples condiciones OR concatenadas dinámicamente (lo que además es propenso a inyección SQL si no se maneja bien), puedes pasar todas las opciones como un array JSON.

SET @intereses_usuario = '["premium", "bluetooth"]';
 
SELECT nombre, precio, etiquetas
FROM productos
WHERE JSON_OVERLAPS(etiquetas, @intereses_usuario);
nombreprecioetiquetas
iPhone 15 Pro1299.99["smartphone", "apple", "5g", "premium"]
Sony WH-1000XM5349.00["auriculares", "sony", "bluetooth", "premium"]

La consulta devuelve productos que tengan la etiqueta "premium" o "bluetooth" (o ambas). Es una forma elegante de implementar filtros tipo "selecciona los que te interesen" donde el usuario puede marcar múltiples casillas. Para verificar que un producto tenga todas las etiquetas seleccionadas (en lugar de al menos una), usarías JSON_CONTAINS en su lugar.

La ventaja de este enfoque es que el número de criterios puede variar dinámicamente sin modificar la estructura de la consulta SQL. Tu aplicación simplemente construye el array JSON con las opciones seleccionadas y lo pasa como parámetro.

Caso práctico: detectar conflictos de horario

Imagina un sistema de reservas de salas de reuniones donde cada reserva almacena las horas ocupadas como un array de enteros. Antes de confirmar una nueva reserva, necesitas verificar que no haya solapamiento con las reservas existentes.

CREATE TABLE reservas_sala (
    id INT AUTO_INCREMENT PRIMARY KEY,
    sala VARCHAR(50),
    fecha DATE,
    empleado VARCHAR(100),
    horas JSON
);
 
INSERT INTO reservas_sala (sala, fecha, empleado, horas) VALUES
('Sala A', '2025-06-15', 'María García', '[9, 10, 11]'),
('Sala A', '2025-06-15', 'Carlos López', '[14, 15, 16]'),
('Sala B', '2025-06-15', 'Ana Martínez', '[10, 11, 12]');

Para verificar si una nueva reserva de la Sala A entre las 11 y las 13 tiene conflicto con reservas existentes, pasas las horas propuestas como un array y usas JSON_OVERLAPS para detectar intersecciones.

SET @nueva_reserva = '[11, 12, 13]';
 
SELECT
    empleado,
    horas,
    JSON_OVERLAPS(horas, @nueva_reserva) AS tiene_conflicto
FROM reservas_sala
WHERE sala = 'Sala A'
  AND fecha = '2025-06-15';
empleadohorastiene_conflicto
María García[9, 10, 11]1
Carlos López[14, 15, 16]0

La reserva de María tiene conflicto porque la hora 11 está en ambos arrays. La de Carlos no tiene conflicto porque sus horas (14, 15, 16) no se solapan con las solicitadas (11, 12, 13). Para bloquear la reserva de forma preventiva, puedes usar la misma función en la cláusula WHERE y contar los conflictos.

SELECT COUNT(*) AS conflictos
FROM reservas_sala
WHERE sala = 'Sala A'
  AND fecha = '2025-06-15'
  AND JSON_OVERLAPS(horas, @nueva_reserva);
conflictos
1

Si el resultado es mayor que cero, la aplicación puede rechazar la nueva reserva o mostrar un mensaje indicando qué empleados tienen reservas que se solapan. Este patrón es mucho más limpio que almacenar cada hora como una fila individual y hacer joins complejos.

Caso práctico: encontrar usuarios con permisos coincidentes

En aplicaciones con sistemas de permisos basados en roles, JSON_OVERLAPS permite verificar rápidamente si un usuario tiene alguno de los permisos necesarios para acceder a un recurso. Esto es útil cuando la autorización no exige todos los permisos, sino al menos uno.

CREATE TABLE usuarios_sistema (
    id INT AUTO_INCREMENT PRIMARY KEY,
    nombre VARCHAR(100),
    email VARCHAR(200),
    permisos JSON
);
 
INSERT INTO usuarios_sistema (nombre, email, permisos) VALUES
('Elena Navarro', 'elena@empresa.com', '["lectura", "escritura", "admin"]'),
('Jorge Ruiz', 'jorge@empresa.com', '["lectura"]'),
('Laura Pérez', 'laura@empresa.com', '["lectura", "escritura"]'),
('Miguel Torres', 'miguel@empresa.com', '["lectura", "reportes"]');

Para encontrar usuarios que puedan editar contenido (necesitan "escritura" o "admin"), defines los permisos requeridos como un array y filtras con JSON_OVERLAPS.

SET @permisos_edicion = '["escritura", "admin"]';
 
SELECT nombre, email, permisos
FROM usuarios_sistema
WHERE JSON_OVERLAPS(permisos, @permisos_edicion);
nombreemailpermisos
Elena Navarroelena@empresa.com["lectura", "escritura", "admin"]
Laura Pérezlaura@empresa.com["lectura", "escritura"]

Solo Elena y Laura tienen al menos uno de los permisos necesarios. Jorge solo tiene "lectura" y Miguel tiene "lectura" y "reportes", ninguno de los cuales coincide con los permisos de edición.

Manejo de NULL y arrays vacíos

El comportamiento de JSON_OVERLAPS con valores nulos y arrays vacíos es predecible pero importante de documentar, porque puede afectar a tus consultas cuando trabajas con datos incompletos.

Si cualquiera de los argumentos es NULL, la función devuelve NULL, no 0. Esta distinción es relevante porque en MySQL, NULL en una condición WHERE se evalúa como falso, pero no es lo mismo que 0 en contextos como CASE o IF.

SELECT
    JSON_OVERLAPS(NULL, '[1, 2, 3]') AS primer_null,
    JSON_OVERLAPS('[1, 2, 3]', NULL) AS segundo_null;
primer_nullsegundo_null
NULLNULL

Con arrays vacíos, el resultado es 0 porque no hay elementos que puedan coincidir. Un array vacío no se solapa con nada, ni siquiera con otro array vacío.

SELECT
    JSON_OVERLAPS('[]', '[1, 2, 3]') AS vacio_con_datos,
    JSON_OVERLAPS('[]', '[]') AS ambos_vacios;
vacio_con_datosambos_vacios
00

Un caso que puede sorprender es que si ambos arrays contienen null como elemento (no NULL de SQL, sino el valor JSON null), sí se detecta solapamiento porque null es un valor JSON válido.

SELECT JSON_OVERLAPS('[1, null, 3]', '[null, 4, 5]') AS resultado;
resultado
1

Rendimiento e índices multi-valor

En tablas pequeñas, JSON_OVERLAPS funciona perfectamente sin optimización adicional. Pero cuando trabajas con tablas de miles o millones de filas, el rendimiento se convierte en una consideración importante. Al igual que MEMBER OF, JSON_OVERLAPS puede aprovechar los índices multi-valor que MySQL 8.0.17 introdujo precisamente para optimizar este tipo de consultas.

Para que el optimizador de MySQL pueda usar un índice multi-valor con JSON_OVERLAPS, uno de los operandos debe ser una columna JSON con un índice multi-valor creado, y el otro debe ser un valor constante o una variable. Si ambos operandos son columnas, el índice no puede utilizarse y MySQL recurrirá a un escaneo completo de la tabla.

ALTER TABLE productos ADD INDEX idx_etiquetas ((CAST(etiquetas AS CHAR(50) ARRAY)));
 
EXPLAIN SELECT nombre FROM productos
WHERE JSON_OVERLAPS(etiquetas, '["premium", "5g"]');

Con el índice creado, el plan de ejecución debería mostrar que MySQL utiliza el índice en lugar de un escaneo de tabla completo. En tablas con millones de filas y arrays de etiquetas, esta diferencia puede reducir el tiempo de consulta de varios segundos a milisegundos.

Errores comunes

El error más frecuente al usar JSON_OVERLAPS es asumir que funciona con cadenas en lugar de con documentos JSON válidos. Si pasas una cadena que no es JSON válido, MySQL lanzará un error.

-- Esto produce un error porque las cadenas no son JSON válido
SELECT JSON_OVERLAPS('smartphone', '["smartphone"]');
-- ERROR: Invalid JSON text

La forma correcta es asegurarte de que ambos argumentos sean documentos JSON válidos. Si quieres comparar un escalar, envuélvelo entre comillas JSON.

SELECT JSON_OVERLAPS('"smartphone"', '["smartphone"]') AS resultado;
resultado
1

Otro error común es confundir JSON_OVERLAPS con JSON_CONTAINS. Recuerda que JSON_OVERLAPS devuelve 1 si hay al menos una coincidencia (lógica OR), mientras que JSON_CONTAINS requiere que todos los elementos del segundo argumento estén presentes (lógica AND). Elegir la función incorrecta puede producir resultados inesperados en tus filtros de búsqueda.

Combinación con otras funciones

Puedes combinar JSON_OVERLAPS con otras funciones JSON para obtener análisis más ricos. Por ejemplo, para generar una matriz de solapamiento entre todos los pares de productos de tu catálogo, combinas un CROSS JOIN con JSON_OVERLAPS para identificar qué pares tienen etiquetas en común.

SELECT
    p1.nombre AS producto_1,
    p2.nombre AS producto_2,
    JSON_OVERLAPS(p1.etiquetas, p2.etiquetas) AS se_solapan
FROM productos p1
CROSS JOIN productos p2
WHERE p1.id < p2.id
  AND JSON_OVERLAPS(p1.etiquetas, p2.etiquetas) = 1
ORDER BY p1.nombre, p2.nombre;
producto_1producto_2se_solapan
Google Pixel 8Samsung Galaxy S241
iPad AirMacBook Air M31
iPhone 15 ProGoogle Pixel 81
iPhone 15 ProiPad Air1
iPhone 15 ProMacBook Air M31
iPhone 15 ProSamsung Galaxy S241
iPhone 15 ProSony WH-1000XM51

Esta consulta genera el grafo completo de relaciones entre productos, lo que puede alimentar un algoritmo de recomendaciones o un análisis de clustering de tu catálogo.

También puedes combinar JSON_OVERLAPS con JSON_LENGTH para priorizar resultados. Por ejemplo, si quieres encontrar no solo los productos que se solapan, sino ordenarlos de forma que los que tienen más etiquetas en total aparezcan primero (bajo la hipótesis de que un producto bien etiquetado es más relevante), puedes añadir un ORDER BY basado en la longitud del array de etiquetas.

Cuándo usar JSON_OVERLAPS

Usa JSON_OVERLAPS siempre que necesites responder a la pregunta "tienen algo en común". Los escenarios ideales incluyen sistemas de recomendación de productos por etiquetas compartidas, filtros de búsqueda donde el usuario selecciona múltiples opciones y quiere resultados que coincidan con al menos una, detección de conflictos de horarios o recursos, validación de permisos donde basta con tener uno de los permisos requeridos, y detección de duplicados parciales entre registros.

Si en cambio necesitas verificar que un documento contenga completamente a otro, usa JSON_CONTAINS. Si solo necesitas comprobar si un valor específico existe en un array, MEMBER OF es más expresiva y legible. Y si necesitas saber exactamente qué elementos comparten dos arrays, tendrás que recurrir a JSON_TABLE para descomponer los arrays y hacer una intersección explícita.

En el siguiente artículo veremos JSON_PRETTY para formatear documentos JSON de forma legible.

Escrito por Eduardo Lázaro