JSON_KEYS

La función JSON_KEYS devuelve las claves de un objeto JSON como un array JSON de cadenas. Es la herramienta principal para descubrir la estructura de un documento JSON sin conocerla de antemano, algo que resulta especialmente útil cuando trabajas con documentos de estructura variable como atributos de productos, configuraciones de usuario o respuestas de APIs externas. En lugar de saber de antemano qué claves tiene el documento, puedes preguntarle directamente.

Sintaxis

JSON_KEYS(documento_json [, ruta])

El primer argumento es el documento JSON. El segundo argumento es opcional y especifica una ruta dentro del documento para obtener las claves de un sub-objeto en lugar de las claves de la raíz. Si se omite la ruta, la función devuelve las claves del objeto raíz.

Comportamiento básico

Obtener las claves de un objeto JSON simple:

SELECT JSON_KEYS('{"nombre": "Monitor 4K", "precio": 349.99, "stock": 45, "activo": true}') AS claves;
claves
["nombre", "precio", "stock", "activo"]

El resultado es un array JSON con los nombres de todas las claves del objeto. El orden de las claves en el array sigue el orden en que MySQL las almacena internamente, que es alfabético en el formato binario.

Si aplicas JSON_KEYS a un array en lugar de un objeto, la función devuelve NULL:

SELECT JSON_KEYS('[1, 2, 3]') AS claves;
claves
NULL

JSON_KEYS solo funciona con objetos JSON. Los arrays no tienen claves, así que el resultado es NULL.

Obtener claves de un sub-objeto

Con el segundo argumento puedes obtener las claves de un objeto anidado:

SELECT JSON_KEYS(
    '{"producto": "Portátil", "specs": {"cpu": "i9", "ram": "32GB", "ssd": "1TB"}, "precio": 1500}',
    '$.specs'
) AS claves_specs;
claves_specs
["cpu", "ram", "ssd"]

La ruta $.specs apunta al objeto anidado, y JSON_KEYS devuelve las claves de ese objeto en particular, no las de la raíz.

Caso práctico: descubrir la estructura de datos variables

En una tienda online, los productos de diferentes categorías tienen atributos distintos. JSON_KEYS permite descubrir qué atributos tiene cada producto:

CREATE TABLE productos (
    id INT AUTO_INCREMENT PRIMARY KEY,
    nombre VARCHAR(200) NOT NULL,
    categoria VARCHAR(50),
    atributos JSON
);
 
INSERT INTO productos (nombre, categoria, atributos) VALUES
('MacBook Air M3', 'portatiles', '{"marca": "Apple", "procesador": "M3", "ram": "16GB", "ssd": "512GB", "peso_kg": 1.24}'),
('iPhone 15 Pro', 'moviles', '{"marca": "Apple", "chip": "A17 Pro", "pantalla": "6.1 pulgadas", "almacenamiento": "256GB", "5g": true}'),
('Camiseta running', 'ropa', '{"marca": "Nike", "talla": "M", "color": "Negro", "material": "Dri-FIT", "genero": "Hombre"}'),
('Sartén 28cm', 'hogar', '{"marca": "Tefal", "diametro_cm": 28, "material": "Aluminio", "antiadherente": true, "apta_induccion": true}');
SELECT nombre, categoria, JSON_KEYS(atributos) AS campos
FROM productos;
nombrecategoriacampos
MacBook Air M3portatiles["marca", "peso_kg", "procesador", "ram", "ssd"]
iPhone 15 Promoviles["5g", "almacenamiento", "chip", "marca", "pantalla"]
Camiseta runningropa["color", "genero", "marca", "material", "talla"]
Sartén 28cmhogar["antiadherente", "apta_induccion", "diametro_cm", "marca", "material"]

Cada categoría tiene campos completamente diferentes (excepto marca, que es común). JSON_KEYS permite a la aplicación descubrir dinámicamente qué campos mostrar para cada producto sin tener que codificar las claves de antemano.

Caso práctico: comparar esquemas entre filas

JSON_KEYS es útil para verificar si todos los documentos de una categoría tienen la misma estructura:

INSERT INTO productos (nombre, categoria, atributos) VALUES
('ThinkPad X1', 'portatiles', '{"marca": "Lenovo", "procesador": "i7", "ram": "32GB", "ssd": "1TB", "peso_kg": 1.12, "pantalla": "14 pulgadas"}');
SELECT nombre,
       JSON_KEYS(atributos) AS campos,
       JSON_LENGTH(JSON_KEYS(atributos)) AS num_campos
FROM productos
WHERE categoria = 'portatiles';
nombrecamposnum_campos
MacBook Air M3["marca", "peso_kg", "procesador", "ram", "ssd"]5
ThinkPad X1["marca", "pantalla", "peso_kg", "procesador", "ram", "ssd"]6

El ThinkPad tiene un campo adicional (pantalla) que el MacBook Air no tiene. Este tipo de análisis es valioso para detectar inconsistencias en los datos o para generar formularios dinámicos que se adapten a la estructura real de cada documento.

Caso práctico: auditoría de configuraciones

En un sistema con configuraciones por usuario, puedes usar JSON_KEYS para verificar qué usuarios han configurado ciertos ajustes:

CREATE TABLE preferencias (
    usuario_id INT PRIMARY KEY,
    config JSON NOT NULL DEFAULT ('{}')
);
 
INSERT INTO preferencias VALUES
(1, '{"tema": "oscuro", "idioma": "es", "notificaciones": {"email": true, "push": false}, "moneda": "EUR"}'),
(2, '{"tema": "claro", "idioma": "en"}'),
(3, '{"tema": "oscuro", "idioma": "es", "notificaciones": {"email": true, "push": true}, "moneda": "USD", "zona_horaria": "America/Mexico_City"}');
SELECT
    usuario_id,
    JSON_KEYS(config) AS ajustes_configurados,
    JSON_LENGTH(JSON_KEYS(config)) AS total_ajustes
FROM preferencias;
usuario_idajustes_configuradostotal_ajustes
1["idioma", "moneda", "notificaciones", "tema"]4
2["idioma", "tema"]2
3["idioma", "moneda", "notificaciones", "tema", "zona_horaria"]5

El usuario 2 solo tiene dos ajustes básicos, mientras que el usuario 3 tiene una configuración completa. Puedes usar esta información para identificar usuarios que aún no han personalizado su experiencia.

Para examinar las claves del sub-objeto de notificaciones:

SELECT
    usuario_id,
    JSON_KEYS(config, '$.notificaciones') AS canales_notificacion
FROM preferencias
WHERE JSON_CONTAINS_PATH(config, 'one', '$.notificaciones');
usuario_idcanales_notificacion
1["email", "push"]
3["email", "push"]

El usuario 2 no aparece porque no tiene la clave notificaciones en su configuración.

Encontrar claves exclusivas de ciertos documentos

Puedes usar JSON_KEYS combinada con otras funciones para buscar documentos que tengan claves específicas:

SELECT nombre, categoria
FROM productos
WHERE JSON_CONTAINS(
    JSON_KEYS(atributos),
    '"antiadherente"'
);
nombrecategoria
Sartén 28cmhogar

Esta consulta encuentra productos que tienen la clave antiadherente en sus atributos. JSON_KEYS devuelve el array de claves y JSON_CONTAINS verifica si ese array incluye la clave buscada.

Manejo de NULL

Cuando el documento JSON es NULL, JSON_KEYS devuelve NULL:

SELECT JSON_KEYS(NULL) AS resultado;
resultado
NULL

Cuando la ruta proporcionada no apunta a un objeto, también devuelve NULL:

SELECT
    JSON_KEYS('{"a": [1, 2, 3]}', '$.a') AS claves_de_array,
    JSON_KEYS('{"a": "texto"}', '$.a') AS claves_de_escalar,
    JSON_KEYS('{"a": 1}', '$.b') AS ruta_inexistente;
claves_de_arrayclaves_de_escalarruta_inexistente
NULLNULLNULL

En los tres casos el resultado es NULL: un array no tiene claves, un valor escalar tampoco, y una ruta inexistente no tiene nada. Es importante verificar que el valor en la ruta sea un objeto antes de asumir que JSON_KEYS devolverá un array.

Combinación con otras funciones

JSON_KEYS se combina bien con JSON_LENGTH para contar el número de claves, con JSON_CONTAINS para buscar claves específicas, y con JSON_TABLE para convertir las claves en filas:

SELECT
    nombre,
    clave.valor AS atributo
FROM productos,
JSON_TABLE(
    JSON_KEYS(atributos),
    '$[*]' COLUMNS (valor VARCHAR(50) PATH '$')
) AS clave
WHERE categoria = 'portatiles'
ORDER BY nombre, clave.valor;
nombreatributo
MacBook Air M3marca
MacBook Air M3peso_kg
MacBook Air M3procesador
MacBook Air M3ram
MacBook Air M3ssd
ThinkPad X1marca
ThinkPad X1pantalla
ThinkPad X1peso_kg
ThinkPad X1procesador
ThinkPad X1ram
ThinkPad X1ssd

Esta consulta usa JSON_TABLE para convertir el array de claves en filas individuales, lo que permite hacer operaciones como GROUP BY o DISTINCT sobre los nombres de las claves presentes en todos los documentos de una categoría.

En el siguiente artículo veremos JSON_CONTAINS para buscar valores dentro de documentos JSON.

Escrito por Eduardo Lázaro