MEMBER OF
El operador MEMBER OF verifica si un valor escalar es miembro de un array JSON. A diferencia de JSON_CONTAINS, que compara documentos JSON completos, MEMBER OF está diseñado específicamente para comprobar la pertenencia de un valor simple a un array. Además, puede aprovechar los índices multi-valor de MySQL 8.0 para realizar búsquedas eficientes en arrays JSON almacenados en columnas.
Sintaxis
valor MEMBER OF(expresion_json_array)El operador se escribe con el valor a buscar a la izquierda y el array JSON entre paréntesis a la derecha. Devuelve 1 (verdadero) si el valor es un elemento del array, y 0 (falso) en caso contrario. La comparación respeta los tipos: el entero 5 no es igual a la cadena "5".
Es importante notar que MEMBER OF requiere MySQL 8.0.17 o superior. El valor de la izquierda se convierte automáticamente a JSON para la comparación, por lo que un entero SQL 5 se compara con el valor JSON 5, y una cadena SQL 'hola' se compara con la cadena JSON "hola".
Comportamiento básico
Veamos cómo funciona con arrays literales:
SELECT 3 MEMBER OF('[1, 2, 3, 4, 5]') AS encontrado;| encontrado |
|---|
| 1 |
El valor 3 es miembro del array, por lo que devuelve 1. Si buscamos un valor que no existe:
SELECT 7 MEMBER OF('[1, 2, 3, 4, 5]') AS encontrado;| encontrado |
|---|
| 0 |
La comparación de tipos es estricta. Un entero y su representación como cadena no son iguales:
SELECT 5 MEMBER OF('["5", "10", "15"]') AS como_entero,
'5' MEMBER OF('["5", "10", "15"]') AS como_cadena_sql,
CAST('"5"' AS JSON) MEMBER OF('["5", "10", "15"]') AS como_cadena_json;| como_entero | como_cadena_sql | como_cadena_json |
|---|---|---|
| 0 | 0 | 1 |
Este resultado merece atención. El entero 5 no coincide con la cadena JSON "5", lo cual es esperable. Pero la cadena SQL '5' tampoco coincide directamente. Para comparar correctamente una cadena SQL con un string JSON en el array, necesitas usar CAST('"5"' AS JSON) o simplemente usar la forma correcta. En la práctica, cuando trabajas con columnas de tabla, MySQL maneja las conversiones de forma natural para enteros y otros tipos numéricos.
Caso práctico: filtrar productos por etiquetas
Imagina una tabla de productos donde cada producto tiene un array JSON de etiquetas:
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"]'),
('MacBook Air M3', 1399.00, '["portatil", "apple", "ultrabook"]'),
('Lenovo ThinkPad X1', 1549.00, '["portatil", "lenovo", "empresarial"]'),
('AirPods Pro', 279.00, '["auriculares", "apple", "bluetooth"]'),
('Sony WH-1000XM5', 349.00, '["auriculares", "sony", "bluetooth", "premium"]');Para encontrar todos los productos de Apple:
SELECT nombre, precio
FROM productos
WHERE 'apple' MEMBER OF(etiquetas);| nombre | precio |
|---|---|
| iPhone 15 Pro | 1299.99 |
| MacBook Air M3 | 1399.00 |
| AirPods Pro | 279.00 |
La consulta es limpia y legible: selecciona los productos donde 'apple' es miembro del array etiquetas. Puedes combinar múltiples condiciones MEMBER OF:
SELECT nombre, precio
FROM productos
WHERE 'apple' MEMBER OF(etiquetas)
AND 'premium' MEMBER OF(etiquetas);| nombre | precio |
|---|---|
| iPhone 15 Pro | 1299.99 |
Solo el iPhone 15 Pro tiene ambas etiquetas: apple y premium.
Comparación con JSON_CONTAINS
MEMBER OF y JSON_CONTAINS pueden lograr resultados similares, pero la sintaxis y el enfoque son diferentes:
-- Con MEMBER OF
SELECT nombre FROM productos
WHERE 'smartphone' MEMBER OF(etiquetas);
-- Equivalente con JSON_CONTAINS
SELECT nombre FROM productos
WHERE JSON_CONTAINS(etiquetas, '"smartphone"');Ambas consultas devuelven los mismos resultados. La diferencia principal es que JSON_CONTAINS requiere que el segundo argumento sea un valor JSON válido (de ahí las comillas dobles dentro de las simples: '"smartphone"'), mientras que MEMBER OF acepta un valor SQL normal y lo convierte automáticamente.
Otra diferencia es que JSON_CONTAINS puede comparar documentos JSON complejos (objetos contra objetos, arrays contra arrays), mientras que MEMBER OF solo compara valores escalares contra un array.
Caso práctico: índices multi-valor
Una de las ventajas más importantes de MEMBER OF es que puede aprovechar los índices multi-valor introducidos en MySQL 8.0.17. Estos índices permiten búsquedas eficientes dentro de arrays JSON:
CREATE TABLE articulos (
id INT AUTO_INCREMENT PRIMARY KEY,
titulo VARCHAR(200),
categorias JSON,
INDEX idx_categorias ((CAST(categorias AS UNSIGNED ARRAY)))
);
INSERT INTO articulos (titulo, categorias) VALUES
('Guía de MySQL 8', '[1, 3, 5]'),
('Introducción a Python', '[2, 4]'),
('Docker para principiantes', '[1, 2, 6]'),
('Seguridad en bases de datos', '[1, 3]'),
('Machine Learning básico', '[2, 5, 7]');El índice se crea usando CAST(columna AS tipo ARRAY). Ahora las consultas con MEMBER OF usan el índice automáticamente:
SELECT titulo
FROM articulos
WHERE 1 MEMBER OF(categorias);| titulo |
|---|
| Guía de MySQL 8 |
| Docker para principiantes |
| Seguridad en bases de datos |
Puedes verificar que se usa el índice con EXPLAIN:
EXPLAIN SELECT titulo FROM articulos WHERE 1 MEMBER OF(categorias);En el plan de ejecución verás que MySQL utiliza el índice idx_categorias en lugar de un escaneo completo de la tabla. Para tablas con millones de filas, esta diferencia es enorme.
Caso práctico: sistema de permisos
Un patrón común es almacenar roles o permisos como arrays JSON y verificar la pertenencia:
CREATE TABLE usuarios (
id INT AUTO_INCREMENT PRIMARY KEY,
nombre VARCHAR(100),
email VARCHAR(200),
roles JSON
);
INSERT INTO usuarios (nombre, email, roles) VALUES
('María García', 'maria@empresa.com', '["admin", "editor", "moderador"]'),
('Carlos López', 'carlos@empresa.com', '["editor", "autor"]'),
('Ana Martínez', 'ana@empresa.com', '["autor"]'),
('Pedro Sánchez', 'pedro@empresa.com', '["admin", "soporte"]');Para encontrar todos los administradores:
SELECT nombre, email
FROM usuarios
WHERE 'admin' MEMBER OF(roles);| nombre | |
|---|---|
| María García | maria@empresa.com |
| Pedro Sánchez | pedro@empresa.com |
Para verificar si un usuario específico tiene un permiso determinado:
SELECT
nombre,
'admin' MEMBER OF(roles) AS es_admin,
'editor' MEMBER OF(roles) AS es_editor,
'autor' MEMBER OF(roles) AS es_autor
FROM usuarios;| nombre | es_admin | es_editor | es_autor |
|---|---|---|---|
| María García | 1 | 1 | 0 |
| Carlos López | 0 | 1 | 1 |
| Ana Martínez | 0 | 0 | 1 |
| Pedro Sánchez | 1 | 0 | 0 |
Este enfoque permite consultar permisos de forma eficiente y legible sin necesidad de tablas intermedias de relación muchos-a-muchos.
Manejo de NULL
Si el valor a buscar es NULL, MEMBER OF devuelve NULL:
SELECT NULL MEMBER OF('[1, 2, 3]') AS resultado;| resultado |
|---|
| NULL |
Si el array JSON es NULL, el resultado también es NULL:
SELECT 1 MEMBER OF(NULL) AS resultado;| resultado |
|---|
| NULL |
Sin embargo, si el array contiene null como elemento y buscas un null JSON, sí se encuentra:
SELECT CAST('null' AS JSON) MEMBER OF('[1, null, 3]') AS resultado;| resultado |
|---|
| 1 |
Combinación con otras funciones
Puedes usar MEMBER OF junto con otras funciones JSON y operadores SQL para consultas más avanzadas. Por ejemplo, para encontrar productos que tengan cierta etiqueta y contar cuántas etiquetas tienen en total:
SELECT
nombre,
precio,
JSON_LENGTH(etiquetas) AS num_etiquetas
FROM productos
WHERE 'bluetooth' MEMBER OF(etiquetas)
ORDER BY precio;| nombre | precio | num_etiquetas |
|---|---|---|
| AirPods Pro | 279.00 | 3 |
| Sony WH-1000XM5 | 349.00 | 4 |
También funciona bien en expresiones CASE:
SELECT
nombre,
CASE
WHEN 'premium' MEMBER OF(etiquetas) THEN 'Gama Alta'
WHEN 'empresarial' MEMBER OF(etiquetas) THEN 'Profesional'
ELSE 'Estándar'
END AS segmento
FROM productos;| nombre | segmento |
|---|---|
| iPhone 15 Pro | Gama Alta |
| Samsung Galaxy S24 | Estándar |
| MacBook Air M3 | Estándar |
| Lenovo ThinkPad X1 | Profesional |
| AirPods Pro | Estándar |
| Sony WH-1000XM5 | Gama Alta |
En el siguiente artículo veremos JSON_OVERLAPS para detectar elementos comunes entre documentos JSON.
Escrito por Eduardo Lázaro
