JSON_LENGTH
La función JSON_LENGTH cuenta el número de elementos en un valor JSON. Si el valor es un array, devuelve la cantidad de elementos que contiene. Si es un objeto, devuelve la cantidad de pares clave-valor. Si es un valor escalar (cadena, número, booleano o null), devuelve 1. Esta función es una de las herramientas de análisis más utilizadas cuando trabajas con datos JSON en MySQL, ya que permite responder preguntas fundamentales sobre la estructura de tus documentos: cuántos productos tiene un carrito, cuántas imágenes se han subido, cuántos permisos tiene un usuario o cuántos campos contiene una configuración.
Más allá de la simple inspección, JSON_LENGTH se convierte en una pieza clave para implementar lógica de negocio directamente en la base de datos. Puedes usarla para establecer límites en la cantidad de elementos que un array puede contener, para filtrar registros según el tamaño de sus colecciones internas, para categorizar documentos según su nivel de completitud o para generar estadísticas agregadas sobre la complejidad de tus datos JSON. Su combinación con funciones de agregación como AVG, MAX y COUNT abre un abanico de posibilidades para el análisis de datos semiestructurados.
Sintaxis
La función acepta uno o dos argumentos. El primero es obligatorio y corresponde al documento JSON que deseas medir. El segundo es opcional y permite especificar una ruta dentro del documento para medir un subelemento en lugar del documento completo.
JSON_LENGTH(json_doc)
JSON_LENGTH(json_doc, ruta)Cuando proporcionas solo el primer argumento, JSON_LENGTH evalúa el elemento raíz del documento. Cuando proporcionas una ruta como segundo argumento, la función navega hasta esa ubicación dentro del documento y mide el elemento que encuentra allí. Si la ruta especificada no existe en el documento, la función devuelve NULL en lugar de 0. Esta distinción entre "la ruta existe pero el elemento está vacío" (devuelve 0) y "la ruta no existe" (devuelve NULL) es importante para escribir consultas correctas, especialmente cuando trabajas con documentos que pueden tener estructuras heterogéneas.
Comportamiento básico con arrays
Cuando aplicas JSON_LENGTH a un array, la función cuenta el número de elementos que contiene, independientemente del tipo de cada elemento. Un array puede contener cadenas, números, booleanos, objetos, otros arrays o valores null, y cada uno cuenta como un único elemento.
SELECT JSON_LENGTH('[10, 20, 30, 40, 50]') AS longitud;| longitud |
|---|
| 5 |
Un array vacío tiene longitud cero, lo que resulta lógico ya que no contiene ningún elemento. Este valor es especialmente útil para detectar colecciones que se inicializaron pero nunca recibieron datos.
SELECT JSON_LENGTH('[]') AS array_vacio;| array_vacio |
|---|
| 0 |
Es importante entender que JSON_LENGTH solo cuenta los elementos en el nivel inmediato del array. Si un elemento del array es a su vez un objeto o un array anidado, cuenta como un solo elemento, sin importar cuántos sub-elementos contenga internamente.
SELECT JSON_LENGTH('[1, [2, 3, 4], {"a": 5}]') AS longitud;| longitud |
|---|
| 3 |
El array contiene tres elementos: el número 1, un sub-array con tres números y un objeto con una clave. Cada uno de ellos cuenta como una unidad.
Comportamiento básico con objetos
Con un objeto JSON, JSON_LENGTH cuenta la cantidad de pares clave-valor que contiene en su nivel superior. Al igual que con los arrays, los valores anidados se cuentan como una sola unidad.
SELECT JSON_LENGTH('{"nombre": "iPhone", "precio": 1299, "color": "negro"}') AS longitud;| longitud |
|---|
| 3 |
Un objeto vacío tiene longitud cero, lo que permite detectar documentos JSON que existen pero no contienen información útil.
SELECT JSON_LENGTH('{}') AS objeto_vacio;| objeto_vacio |
|---|
| 0 |
Comportamiento con valores escalares
Los valores escalares JSON (cadenas, números, booleanos y null) siempre tienen longitud 1, independientemente de su contenido o tamaño. Una cadena de texto de mil caracteres tiene la misma longitud JSON que el número 0. Esto puede resultar contraintuitivo si esperas que JSON_LENGTH mida la longitud de una cadena como lo haría la función LENGTH de SQL, pero la lógica es coherente: un escalar es un solo valor JSON, por lo tanto su longitud es 1.
SELECT
JSON_LENGTH('"una cadena larga de texto"') AS cadena,
JSON_LENGTH('42') AS numero,
JSON_LENGTH('true') AS booleano,
JSON_LENGTH('null') AS nulo;| cadena | numero | booleano | nulo |
|---|---|---|---|
| 1 | 1 | 1 | 1 |
Si necesitas medir la longitud en caracteres de una cadena almacenada dentro de un documento JSON, debes extraerla primero con JSON_EXTRACT o el operador ->> y luego aplicar CHAR_LENGTH o LENGTH sobre el resultado.
Uso del segundo argumento: medir subelementos
El verdadero poder de JSON_LENGTH se manifiesta cuando utilizas el segundo argumento para inspeccionar subelementos específicos dentro de un documento complejo. En lugar de medir todo el documento, puedes apuntar directamente al array o al objeto que te interesa usando la sintaxis de JSON Path.
SELECT
JSON_LENGTH('{"tags": ["a", "b", "c"], "specs": {"ram": 8, "cpu": "A17"}}', '$.tags') AS len_tags,
JSON_LENGTH('{"tags": ["a", "b", "c"], "specs": {"ram": 8, "cpu": "A17"}}', '$.specs') AS len_specs;| len_tags | len_specs |
|---|---|
| 3 | 2 |
El array tags contiene tres elementos y el objeto specs contiene dos pares clave-valor. Si la ruta apuntara a un valor escalar dentro de specs, el resultado sería 1. Y si la ruta no existiera en el documento, el resultado sería NULL.
Caso práctico: análisis de catálogo de productos
Imaginemos un catálogo de tienda en línea donde cada producto almacena sus imágenes, etiquetas de búsqueda y especificaciones técnicas en una columna JSON. Esta estructura es habitual en plataformas de comercio electrónico porque cada categoría de producto puede tener especificaciones completamente diferentes, algo que las columnas relacionales tradicionales manejan con dificultad.
CREATE TABLE productos (
id INT PRIMARY KEY AUTO_INCREMENT,
nombre VARCHAR(100),
datos JSON
);
INSERT INTO productos (nombre, datos) VALUES
('iPhone 15 Pro',
'{"imagenes": ["frontal.jpg", "trasera.jpg", "lateral.jpg", "detalle.jpg"], "etiquetas": ["smartphone", "apple", "5g", "premium"], "specs": {"ram": 8, "almacenamiento": 256, "pantalla": 6.1, "bateria": 3274, "camara": "48MP"}}'),
('Funda silicona',
'{"imagenes": ["principal.jpg"], "etiquetas": ["accesorio"], "specs": {"material": "silicona", "compatible": "iPhone 15"}}'),
('Samsung Galaxy S24',
'{"imagenes": ["frontal.jpg", "trasera.jpg"], "etiquetas": ["smartphone", "samsung", "5g"], "specs": {"ram": 12, "almacenamiento": 256, "pantalla": 6.2}}'),
('MacBook Air M3',
'{"imagenes": ["abierto.jpg", "cerrado.jpg", "perfil.jpg"], "etiquetas": ["portatil", "apple", "ultrabook"], "specs": {"ram": 16, "almacenamiento": 512, "pantalla": 13.6, "bateria": 52.6, "peso": 1.24, "cpu": "M3"}}');Con la tabla cargada, podemos generar un informe que muestre cuántas imágenes, etiquetas y campos de especificaciones tiene cada producto. Esta vista panorámica permite identificar rápidamente qué productos tienen información incompleta y necesitan atención.
SELECT
nombre,
JSON_LENGTH(datos, '$.imagenes') AS num_imagenes,
JSON_LENGTH(datos, '$.etiquetas') AS num_etiquetas,
JSON_LENGTH(datos, '$.specs') AS num_specs
FROM productos;| nombre | num_imagenes | num_etiquetas | num_specs |
|---|---|---|---|
| iPhone 15 Pro | 4 | 4 | 5 |
| Funda silicona | 1 | 1 | 2 |
| Samsung Galaxy S24 | 2 | 3 | 3 |
| MacBook Air M3 | 3 | 3 | 6 |
El informe revela inmediatamente que la funda de silicona tiene solo una imagen y una etiqueta, lo que probablemente afecta su visibilidad en los resultados de búsqueda y su capacidad de conversión en la página del producto.
Para filtrar específicamente los productos que necesitan más fotografías, basta con usar JSON_LENGTH en la cláusula WHERE. En una tienda real, la cantidad mínima de imágenes por producto suele ser un requisito de calidad impuesto por el equipo de producto.
SELECT nombre, JSON_LENGTH(datos, '$.imagenes') AS imagenes
FROM productos
WHERE JSON_LENGTH(datos, '$.imagenes') < 3;| nombre | imagenes |
|---|---|
| Funda silicona | 1 |
| Samsung Galaxy S24 | 2 |
Caso práctico: control de calidad de datos
Uno de los usos más valiosos de JSON_LENGTH es la clasificación automática de documentos según su nivel de completitud. En un catálogo de productos, no todos los artículos necesitan el mismo nivel de detalle en sus especificaciones. Un smartphone con cinco campos técnicos está razonablemente bien documentado, mientras que un accesorio con solo dos campos podría necesitar enriquecimiento. Usando JSON_LENGTH dentro de una expresión CASE, puedes categorizar automáticamente cada producto.
SELECT
nombre,
JSON_LENGTH(datos, '$.specs') AS campos_specs,
CASE
WHEN JSON_LENGTH(datos, '$.specs') >= 5 THEN 'Completo'
WHEN JSON_LENGTH(datos, '$.specs') >= 3 THEN 'Parcial'
ELSE 'Minimo'
END AS nivel_detalle
FROM productos;| nombre | campos_specs | nivel_detalle |
|---|---|---|
| iPhone 15 Pro | 5 | Completo |
| Funda silicona | 2 | Minimo |
| Samsung Galaxy S24 | 3 | Parcial |
| MacBook Air M3 | 6 | Completo |
Esta clasificación puede alimentar dashboards internos que muestren al equipo de contenido qué productos requieren más trabajo. También puedes usarla para detectar registros con arrays vacíos que podrían indicar datos incompletos o errores en la importación.
SELECT nombre
FROM productos
WHERE JSON_LENGTH(datos, '$.etiquetas') = 0
OR JSON_LENGTH(datos, '$.imagenes') = 0;Esta consulta encontraría productos sin etiquetas o sin imágenes, una situación que en una tienda en línea real podría impedir que el producto aparezca en los resultados de búsqueda o se muestre correctamente en la página de categoría.
Caso práctico: límites en arrays JSON
Cuando almacenas listas de elementos en columnas JSON, JSON_LENGTH te permite implementar verificaciones de capacidad que tu aplicación puede consultar antes de intentar agregar nuevos elementos. Consideremos una tabla de listas de deseos donde cada usuario puede guardar hasta cinco productos.
CREATE TABLE listas_deseos (
id INT PRIMARY KEY AUTO_INCREMENT,
usuario VARCHAR(100),
productos JSON DEFAULT ('[]')
);
INSERT INTO listas_deseos (usuario, productos) VALUES
('Maria Garcia', '["iPhone 15 Pro", "AirPods Pro", "Apple Watch", "iPad Air", "MacBook Air"]'),
('Carlos Lopez', '["Samsung Galaxy S24", "Galaxy Buds"]'),
('Ana Martinez', '["Kindle Paperwhite"]');La siguiente consulta genera un resumen del estado de cada lista, indicando cuántos productos contiene y si el usuario puede seguir añadiendo elementos o ha alcanzado el límite.
SELECT
usuario,
JSON_LENGTH(productos) AS items_en_lista,
CASE
WHEN JSON_LENGTH(productos) >= 5 THEN 'Lista llena (max. 5)'
ELSE CONCAT('Puede anadir ', 5 - JSON_LENGTH(productos), ' mas')
END AS estado
FROM listas_deseos;| usuario | items_en_lista | estado |
|---|---|---|
| Maria Garcia | 5 | Lista llena (max. 5) |
| Carlos Lopez | 2 | Puede anadir 3 mas |
| Ana Martinez | 1 | Puede anadir 4 mas |
Antes de insertar un nuevo producto en la lista de un usuario, tu aplicación puede ejecutar una consulta rápida para verificar si hay espacio disponible. Este patrón resulta mucho más eficiente que extraer todo el array, contar los elementos en el lado de la aplicación y luego decidir si procede la inserción.
SELECT
usuario,
IF(JSON_LENGTH(productos) < 5, 'Permitir', 'Rechazar') AS accion
FROM listas_deseos
WHERE usuario = 'Maria Garcia';| usuario | accion |
|---|---|
| Maria Garcia | Rechazar |
Este enfoque también se aplica a otros escenarios similares: limitar la cantidad de direcciones de envío por cliente, restringir el número de métodos de pago almacenados o controlar la cantidad de etiquetas asignadas a un contenido.
Manejo de NULL y rutas inexistentes
El comportamiento de JSON_LENGTH con valores nulos y rutas que no existen es un aspecto que conviene dominar para evitar resultados inesperados en tus consultas.
Cuando el documento JSON completo es NULL, la función devuelve NULL. Esto ocurre, por ejemplo, cuando una fila tiene la columna JSON sin valor asignado.
SELECT JSON_LENGTH(NULL) AS resultado;| resultado |
|---|
| NULL |
Cuando el documento existe pero la ruta especificada en el segundo argumento no se encuentra dentro del documento, la función también devuelve NULL. Este comportamiento es diferente al de un array o un objeto vacío, que devolvería 0.
SELECT JSON_LENGTH('{"nombre": "Test"}', '$.inexistente') AS resultado;| resultado |
|---|
| NULL |
Esta distinción tiene implicaciones importantes cuando usas JSON_LENGTH en comparaciones dentro de cláusulas WHERE. Una condición como JSON_LENGTH(col, '$.ruta') > 3 excluirá automáticamente las filas donde la ruta no exista, ya que NULL > 3 evalúa a NULL (que en contexto booleano se trata como falso). Si necesitas incluir esas filas en ciertos escenarios, deberás usar COALESCE o IFNULL para proporcionar un valor por defecto.
SELECT nombre
FROM productos
WHERE COALESCE(JSON_LENGTH(datos, '$.reviews'), 0) > 3;En esta consulta, si un producto no tiene la clave reviews en su JSON, COALESCE convierte el NULL en 0, permitiendo que la comparación se evalúe correctamente.
Combinación con funciones de agregación
Una de las aplicaciones más potentes de JSON_LENGTH surge al combinarla con funciones de agregación para obtener estadísticas sobre colecciones de documentos JSON. Esto permite responder preguntas a nivel de tabla, no de fila individual.
SELECT
AVG(JSON_LENGTH(datos, '$.specs')) AS promedio_specs,
MAX(JSON_LENGTH(datos, '$.imagenes')) AS max_imagenes,
MIN(JSON_LENGTH(datos, '$.etiquetas')) AS min_etiquetas,
SUM(JSON_LENGTH(datos, '$.imagenes')) AS total_imagenes
FROM productos;| promedio_specs | max_imagenes | min_etiquetas | total_imagenes |
|---|---|---|---|
| 4.0000 | 4 | 1 | 10 |
Estos datos agregados son valiosos para entender el estado general de tu catálogo: en promedio, cada producto tiene cuatro especificaciones, el producto con más imágenes tiene cuatro y el que menos etiquetas tiene solo una. El total de imágenes puede ser útil para estimar el almacenamiento necesario.
Combinación con JSON_TYPE
También es habitual combinar JSON_LENGTH con JSON_TYPE para construir consultas que verifiquen tanto el tipo de un valor como su tamaño. Esta combinación resulta especialmente útil cuando no puedes garantizar que un campo siempre contenga el mismo tipo de dato en todos los documentos.
SELECT
nombre,
CASE
WHEN JSON_TYPE(datos->'$.specs') = 'OBJECT' AND JSON_LENGTH(datos, '$.specs') > 0
THEN CONCAT(JSON_LENGTH(datos, '$.specs'), ' especificaciones')
ELSE 'Sin especificaciones'
END AS estado_specs
FROM productos;| nombre | estado_specs |
|---|---|
| iPhone 15 Pro | 5 especificaciones |
| Funda silicona | 2 especificaciones |
| Samsung Galaxy S24 | 3 especificaciones |
| MacBook Air M3 | 6 especificaciones |
Verificar el tipo antes de medir la longitud previene errores sutiles. Si por alguna razón el campo specs contuviera una cadena en lugar de un objeto (por ejemplo, "pendiente de cargar"), JSON_LENGTH devolvería 1 y la consulta mostraría "1 especificaciones", lo cual sería engañoso. Al comprobar primero que el tipo es OBJECT, te aseguras de que el conteo es significativo.
Errores comunes
El error más frecuente al usar JSON_LENGTH es confundirla con LENGTH o CHAR_LENGTH. Mientras que estas últimas miden la longitud en bytes o caracteres de una cadena, JSON_LENGTH cuenta elementos JSON. Aplicar JSON_LENGTH a una cadena JSON como '"Hola mundo"' devuelve 1, no 10. Si necesitas la longitud del texto, extrae primero el valor con ->> y luego usa CHAR_LENGTH.
Otro error habitual es olvidar que JSON_LENGTH sin segundo argumento mide el nivel raíz del documento. Si tu documento tiene la forma \{"datos": [...]\}, llamar a JSON_LENGTH(col) devuelve 1 (un par clave-valor en el nivel raíz), no la longitud del array interno. Necesitas especificar la ruta: JSON_LENGTH(col, '$.datos').
Un tercer error es asumir que JSON_LENGTH devuelve 0 cuando la ruta no existe. Como hemos visto, devuelve NULL, lo que puede provocar que condiciones WHERE filtren filas de forma inesperada. Siempre es buena práctica verificar primero que la ruta existe, ya sea con JSON_CONTAINS_PATH o mediante COALESCE.
Cuándo usar JSON_LENGTH
Esta función es la herramienta adecuada siempre que necesites tomar decisiones basadas en la cantidad de elementos dentro de un valor JSON. Los escenarios más comunes incluyen la validación de datos de entrada (verificar que un array no exceda un número máximo de elementos), el control de calidad (detectar documentos con información insuficiente), la generación de informes (estadísticas sobre la distribución del contenido) y la implementación de reglas de negocio (limitar carritos de compra, listas de favoritos o perfiles).
JSON_LENGTH te da una visión cuantitativa de tus datos JSON. Pero a veces no solo importa cuántos elementos tiene un documento, sino qué tan profundamente anidado está. En el siguiente artículo veremos JSON_DEPTH para medir la profundidad de anidamiento de los documentos JSON.
Escrito por Eduardo Lázaro
