Error SQL · agregaciones y JOIN

COUNT(*) con LEFT JOIN devuelve 1 en lugar de 0

Si conservas una fila izquierda sin coincidencias mediante LEFT JOIN, COUNT(*) sigue viendo esa fila extendida con NULL. Para contar coincidencias reales, cuenta una columna no nula de la tabla derecha.

Lectura: 11–15 minEjemplo reproduciblePráctica correctiva

Síntoma

Quieres listar todas las entidades de la izquierda y obtener cero cuando no existe ninguna fila relacionada. Sin embargo, una consulta con LEFT JOIN y COUNT(*) devuelve 1 para el grupo sin coincidencias.

Consulta problemática

COUNT(*) cuenta la fila preservada
WITH categorias(id, nombre) AS (
  VALUES (1, 'Portátiles'), (2, 'Audio'), (3, 'Monitores')
),
productos(id, categoria_id) AS (
  VALUES (101, 1), (102, 1), (103, 2)
)
SELECT c.nombre, COUNT(*) AS productos
FROM categorias AS c
LEFT JOIN productos AS p
  ON p.categoria_id = c.id
GROUP BY c.id, c.nombre
ORDER BY c.id;
Resultado comprobado en SQLite 3.46.1
nombreproductos
Portátiles2
Audio1
Monitores1

Por qué ocurre

LEFT JOIN conserva la fila de Monitores aunque no encuentre un producto. Las columnas de p quedan en NULL, pero la fila del resultado existe. COUNT(*) cuenta filas del grupo y por eso devuelve 1.

En cambio, COUNT(expresión) cuenta únicamente las filas donde esa expresión no es NULL. Si eliges una columna de la tabla derecha que sea NOT NULL cuando existe una coincidencia —normalmente su clave primaria—, la fila artificial del LEFT JOIN no se cuenta.

Corrección: cuenta una columna no nula de la tabla derecha

COUNT(p.id)
WITH categorias(id, nombre) AS (
  VALUES (1, 'Portátiles'), (2, 'Audio'), (3, 'Monitores')
),
productos(id, categoria_id) AS (
  VALUES (101, 1), (102, 1), (103, 2)
)
SELECT c.nombre, COUNT(p.id) AS productos
FROM categorias AS c
LEFT JOIN productos AS p
  ON p.categoria_id = c.id
GROUP BY c.id, c.nombre
ORDER BY c.id;
Resultado correcto
nombreproductos
Portátiles2
Audio1
Monitores0

Regla práctica: después de un LEFT JOIN, usa COUNT(*) si de verdad quieres contar filas preservadas del resultado; usa COUNT(tabla_derecha.columna_no_nula) si quieres contar coincidencias reales.

Otra trampa: filtrar la tabla derecha en WHERE

Si además necesitas contar solo productos que cumplan una condición, colocarla en WHERE puede eliminar a Monitores y convertir el efecto del LEFT JOIN en uno equivalente a un INNER JOIN para ese filtro. Cuando quieres conservar el cero, suele ser más claro mover la condición relacionada al ON y continuar contando una columna derecha no nula.

Práctica correctiva

Básica–intermedia · JOIN + COUNT

Cuenta ventas cerradas, incluido cero

Conserva las tres sucursales y cuenta solo sus ventas cerradas. La sucursal Sur no tiene ventas y debe devolver 0.

Qué debes recordar

  • COUNT(*) cuenta filas, incluida la fila preservada por un LEFT JOIN.
  • COUNT(columna) ignora NULL.
  • Cuenta una columna derecha no nula para medir coincidencias reales.
  • Si necesitas conservar grupos con cero, revisa también si un filtro derecho debería estar en ON y no en WHERE.

Conceptos relacionados

Fuentes técnicas consultadas