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 fundamental para analizar la estructura de tus datos JSON, filtrar registros según el tamaño de sus arrays, y verificar que los documentos contengan la cantidad esperada de campos.

Sintaxis

JSON_LENGTH(json_doc [, ruta])

El primer argumento es el documento JSON a evaluar. El segundo argumento es opcional y especifica una ruta dentro del documento. Cuando proporcionas una ruta, JSON_LENGTH mide el elemento que se encuentra en esa ubicación en lugar del documento raíz. Si la ruta no existe en el documento, la función devuelve NULL.

JSON_LENGTH(json_doc)
JSON_LENGTH(json_doc, '$.ruta')

Comportamiento básico

Con un array, JSON_LENGTH cuenta sus elementos:

SELECT JSON_LENGTH('[10, 20, 30, 40, 50]') AS longitud;
longitud
5

Con un objeto, cuenta los pares clave-valor:

SELECT JSON_LENGTH('{"nombre": "iPhone", "precio": 1299, "color": "negro"}') AS longitud;
longitud
3

Un array o un objeto vacíos tienen longitud cero:

SELECT
    JSON_LENGTH('[]') AS array_vacio,
    JSON_LENGTH('{}') AS objeto_vacio;
array_vacioobjeto_vacio
00

Los valores escalares siempre tienen longitud 1, independientemente de su contenido:

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;
cadenanumerobooleanonulo
1111

Cuando usas el segundo argumento para especificar una ruta, la función mide el elemento en esa ubicación:

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_tagslen_specs
32

Caso práctico: análisis de catálogo de productos

Imagina un catálogo donde cada producto tiene arrays de imágenes, etiquetas y un objeto de especificaciones con un número variable de campos:

CREATE TABLE productos (
    id INT PRIMARY KEY AUTO_INCREMENT,
    nombre VARCHAR(100),
    datos JSON
);
 
INSERT INTO productos (nombre, datos) VALUES
('iPhone 15 Pro',
 '{"imágenes": ["frontal.jpg", "trasera.jpg", "lateral.jpg", "detalle.jpg"], "etiquetas": ["smartphone", "apple", "5g", "premium"], "specs": {"ram": 8, "almacenamiento": 256, "pantalla": 6.1, "batería": 3274, "cámara": "48MP"}}'),
('Funda silicona',
 '{"imágenes": ["principal.jpg"], "etiquetas": ["accesorio"], "specs": {"material": "silicona", "compatible": "iPhone 15"}}'),
('Samsung Galaxy S24',
 '{"imágenes": ["frontal.jpg", "trasera.jpg"], "etiquetas": ["smartphone", "samsung", "5g"], "specs": {"ram": 12, "almacenamiento": 256, "pantalla": 6.2}}'),
('MacBook Air M3',
 '{"imágenes": ["abierto.jpg", "cerrado.jpg", "perfil.jpg"], "etiquetas": ["portátil", "apple", "ultrabook"], "specs": {"ram": 16, "almacenamiento": 512, "pantalla": 13.6, "batería": 52.6, "peso": 1.24, "cpu": "M3"}}');

Para ver cuántas imágenes, etiquetas y especificaciones tiene cada producto:

SELECT
    nombre,
    JSON_LENGTH(datos, '$.imágenes') AS num_imágenes,
    JSON_LENGTH(datos, '$.etiquetas') AS num_etiquetas,
    JSON_LENGTH(datos, '$.specs') AS num_specs
FROM productos;
nombrenum_imágenesnum_etiquetasnum_specs
iPhone 15 Pro445
Funda silicona112
Samsung Galaxy S24233
MacBook Air M3336

Para encontrar productos que necesitan más fotografías (menos de 3 imágenes):

SELECT nombre, JSON_LENGTH(datos, '$.imágenes') AS imágenes
FROM productos
WHERE JSON_LENGTH(datos, '$.imágenes') < 3;
nombreimágenes
Funda silicona1
Samsung Galaxy S242

Caso práctico: control de calidad de datos

JSON_LENGTH es muy útil para establecer umbrales de calidad. Puedes categorizar los productos según la completitud de su información:

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 'Mínimo'
    END AS nivel_detalle
FROM productos;
nombrecampos_specsnivel_detalle
iPhone 15 Pro5Completo
Funda silicona2Mínimo
Samsung Galaxy S243Parcial
MacBook Air M36Completo

También puedes usarla para detectar registros con arrays vacíos que podrían indicar datos incompletos:

SELECT nombre
FROM productos
WHERE JSON_LENGTH(datos, '$.etiquetas') = 0
   OR JSON_LENGTH(datos, '$.imágenes') = 0;

Esta consulta encontraría productos sin etiquetas o sin imágenes, lo cual podría ser un problema para la visualización en una tienda en línea.

Caso práctico: paginación y límites en arrays JSON

Cuando almacenas listas de elementos en columnas JSON, JSON_LENGTH te permite implementar verificaciones de límite. Considera una tabla de listas de deseos:

CREATE TABLE listas_deseos (
    id INT PRIMARY KEY AUTO_INCREMENT,
    usuario VARCHAR(100),
    productos JSON DEFAULT ('[]')
);
 
INSERT INTO listas_deseos (usuario, productos) VALUES
('María García', '["iPhone 15 Pro", "AirPods Pro", "Apple Watch", "iPad Air", "MacBook Air"]'),
('Carlos López', '["Samsung Galaxy S24", "Galaxy Buds"]'),
('Ana Martínez', '["Kindle Paperwhite"]');

Para mostrar un resumen de las listas con un conteo:

SELECT
    usuario,
    JSON_LENGTH(productos) AS items_en_lista,
    CASE
        WHEN JSON_LENGTH(productos) >= 5 THEN 'Lista llena (máx. 5)'
        ELSE CONCAT('Puede añadir ', 5 - JSON_LENGTH(productos), ' más')
    END AS estado
FROM listas_deseos;
usuarioitems_en_listaestado
María García5Lista llena (máx. 5)
Carlos López2Puede añadir 3 más
Ana Martínez1Puede añadir 4 más

Para verificar antes de insertar un elemento si la lista ya alcanzó su límite:

SELECT
    usuario,
    IF(JSON_LENGTH(productos) < 5, 'Permitir', 'Rechazar') AS accion
FROM listas_deseos
WHERE usuario = 'María García';
usuarioaccion
María GarcíaRechazar

Manejo de NULL

Cuando el documento JSON es NULL, la función devuelve NULL:

SELECT JSON_LENGTH(NULL) AS resultado;
resultado
NULL

Cuando la ruta especificada no existe en el documento, también devuelve NULL:

SELECT JSON_LENGTH('{"nombre": "Test"}', '$.inexistente') AS resultado;
resultado
NULL

Este comportamiento es importante tenerlo en cuenta cuando usas JSON_LENGTH en comparaciones. 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 (y no a verdadero).

Combinación con otras funciones

JSON_LENGTH se combina frecuentemente con funciones de agregación para obtener estadísticas sobre tus datos JSON:

SELECT
    AVG(JSON_LENGTH(datos, '$.specs')) AS promedio_specs,
    MAX(JSON_LENGTH(datos, '$.imágenes')) AS max_imagenes,
    MIN(JSON_LENGTH(datos, '$.etiquetas')) AS min_etiquetas
FROM productos;
promedio_specsmax_imagenesmin_etiquetas
4.000041

También es habitual usar JSON_LENGTH junto con JSON_TYPE para verificar tanto el tipo como el tamaño:

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;
nombreestado_specs
iPhone 15 Pro5 especificaciones
Funda silicona2 especificaciones
Samsung Galaxy S243 especificaciones
MacBook Air M36 especificaciones

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.

Escrito por Eduardo Lázaro