Subconsultas, agregaciones y valores NULL
Las consultas reales suelen resumir conjuntos de filas, comparar resultados intermedios y trabajar con datos ausentes. Para hacerlo correctamente es necesario comprender las agregaciones, las subconsultas y la lógica especial de NULL.
1. Funciones de agregación #
Las funciones de agregación calculan un resultado a partir de varias filas:
COUNT(*)cuenta filas.COUNT(columna)cuenta valores no nulos.SUMyAVGsuman y calculan la media de valores no nulos.MINyMAXobtienen los extremos.
1SELECT 2 COUNT(*) AS total_pedidos, 3 COUNT(fecha_envio) AS pedidos_enviados, 4 SUM(total) AS importe_total, 5 AVG(total) AS importe_medio 6FROM pedidos;
Si no hay filas, COUNT devuelve 0; otras agregaciones suelen devolver NULL.
2. GROUP BY y HAVING #
GROUP BY reúne filas que comparten valores. Toda columna seleccionada que no forme parte de una agregación debe aparecer normalmente en GROUP BY.
1SELECT cliente_id, COUNT(*) AS numero_pedidos, SUM(total) AS facturacion 2FROM pedidos 3GROUP BY cliente_id;
WHERE filtra filas antes de agrupar. HAVING filtra grupos después de calcular las agregaciones:
1SELECT cliente_id, COUNT(*) AS numero_pedidos 2FROM pedidos 3WHERE estado <> 'cancelado' 4GROUP BY cliente_id 5HAVING COUNT(*) >= 3;
3. Tipos de subconsultas #
Subconsulta escalar #
Devuelve un único valor y puede usarse donde se espera una expresión:
1SELECT nombre 2FROM productos 3WHERE precio > (SELECT AVG(precio) FROM productos);
Si una subconsulta escalar devuelve más de una fila, el motor genera un error.
Subconsulta de conjunto #
Devuelve varias filas y se combina habitualmente con IN, ANY o ALL:
1SELECT nombre 2FROM clientes 3WHERE id IN ( 4 SELECT cliente_id 5 FROM pedidos 6 WHERE estado = 'pendiente' 7);
Subconsulta correlacionada #
Hace referencia a la fila de la consulta exterior:
1SELECT c.nombre 2FROM clientes c 3WHERE EXISTS ( 4 SELECT 1 5 FROM pedidos p 6 WHERE p.cliente_id = c.id 7 AND p.estado = 'pendiente' 8);
4. EXISTS, IN y NOT EXISTS #
Usa EXISTS cuando solo importa saber si existe alguna fila relacionada. Usa IN cuando comparas un valor con una lista pequeña o con un conjunto cuyo significado resulte más claro así. El optimizador puede transformar ambas expresiones, por lo que conviene decidir primero por corrección y legibilidad.
Para buscar clientes sin pedidos, NOT EXISTS es una opción segura:
1SELECT c.id, c.nombre 2FROM clientes c 3WHERE NOT EXISTS ( 4 SELECT 1 5 FROM pedidos p 6 WHERE p.cliente_id = c.id 7);
Evita NOT IN (subconsulta) si el resultado puede contener NULL: una sola presencia de NULL puede hacer que la condición resulte desconocida para todas las filas.
5. La lógica ternaria de NULL #
NULL representa ausencia o desconocimiento, no el número cero ni una cadena vacía. SQL trabaja con tres resultados lógicos: TRUE, FALSE y UNKNOWN.
1-- Incorrecto: nunca comprueba correctamente la ausencia. 2SELECT * FROM clientes WHERE telefono = NULL; 3 4-- Correcto. 5SELECT * FROM clientes WHERE telefono IS NULL;
Una condición WHERE solo conserva las filas cuyo resultado es TRUE; descarta tanto FALSE como UNKNOWN.
También debes considerar que:
- La mayoría de agregaciones ignora los valores
NULL. - En una unión,
NULL = NULLno produce una coincidencia verdadera. - Las reglas de
UNIQUEsobre valoresNULLvarían entre motores. - El orden de los valores nulos puede variar; algunos motores admiten
NULLS FIRSToNULLS LAST.
6. COALESCE y NULLIF #
COALESCE devuelve el primer argumento no nulo:
1SELECT nombre, COALESCE(telefono, 'Sin teléfono') AS telefono_visible 2FROM clientes;
NULLIF(a, b) devuelve NULL si ambos argumentos son iguales. Puede evitar una división entre cero:
1SELECT ingresos / NULLIF(numero_pedidos, 0) AS ingreso_medio 2FROM resumen_clientes;
No uses COALESCE para ocultar diferencias semánticas. Mostrar «Sin teléfono» es apropiado en una salida, pero almacenar ese texto en la columna impediría distinguirlo de un teléfono real.
7. Orden lógico de una consulta #
Para razonar sobre alias, filtros y agregaciones, recuerda este orden conceptual simplificado:
FROMyJOINWHEREGROUP BYHAVINGSELECTDISTINCTORDER BY- Limitación y paginación
El optimizador puede ejecutar un plan diferente, pero debe conservar el resultado definido por esta lógica.
Resumen del tema
Conceptos clave #
WHEREfiltra filas yHAVINGfiltra grupos.- Las subconsultas pueden devolver un valor, un conjunto o depender de la fila exterior.
NOT EXISTSevita la trampa queNULLintroduce en ciertas consultas conNOT IN.NULLparticipa en una lógica de tres valores y se comprueba conIS NULL.COALESCEyNULLIFpermiten tratar ausencias sin convertirlas en valores ficticios almacenados.
Qué debes recordar #
Antes de escribir una consulta compleja, identifica qué se filtra antes de agrupar, qué se calcula después y dónde puede aparecer NULL.