El orden de ejecución de una consulta
En resumen: Una consulta SQL se escribe empezando por SELECT, pero la base la ejecuta en el orden FROM, WHERE, GROUP BY, HAVING, SELECT, ORDER BY, LIMIT.
Problema
Aquí funciona, allí no
En la lección de los grupos, ORDER BY month usó el alias de SELECT. En la lección de HAVING repetimos todo el SUM(...), y SUM en WHERE dio un error. ¿Por qué un alias funciona en un sitio y en otro no?
La respuesta está en el orden en que la base lee la consulta. No es el orden en que tú la escribes.
La idea
Se escribe en un orden, se ejecuta en otro
Tú escribes así:
SELECT → FROM → WHERE → GROUP BY → HAVING → ORDER BY → LIMIT
La base piensa así:
FROM: de dónde vienen las filas;WHERE: qué filas se quedan;GROUP BY: los grupos;HAVING: qué grupos se quedan;SELECT: qué se muestra y cómo se llama;ORDER BY: el orden;LIMIT: cuántas filas.
Este es el orden lógico de ejecución de una consulta. Los tres meses con más movimiento, sin los pedidos cancelados:
Paso a paso: FROM toma los 60 pedidos. WHERE quita los 7 cancelados y quedan 53. GROUP BY los divide en 14 meses. HAVING conserva los seis con al menos 5 pedidos. SELECT calcula el mes y la cuenta y les pone nombre. ORDER BY los ordena, y LIMIT toma los tres primeros.
Más precisamente, para curiosos
Este es el orden en que la consulta debe parecer que se ejecuta. El plan real puede ser distinto: la base puede usar un índice o saltarse un paso, siempre que el resultado sea el mismo. Los planes de consulta los veremos en el módulo de índices.
La idea
Por qué el alias funciona en ORDER BY
El alias nace en SELECT, en el paso 5. ORDER BY es el paso 6, así que el nombre ya existe:
Los tres pedidos más grandes. Esto funciona en todas las bases. En WHERE, GROUP BY y HAVING, según la regla, el alias todavía no existe, porque van antes que SELECT.
Pruébalo
Los clientes más activos
Muestra los cinco clientes con más pedidos: customer_id y la cantidad en una columna llamada orders, el que más primero. En caso de empate va primero el customer_id menor.
Solución
SELECT customer_id, COUNT(*) AS orders
FROM orders
GROUP BY customer_id
ORDER BY orders DESC, customer_id
LIMIT 5;GROUP BY customer_id, COUNT(*) AS orders, ORDER BY orders DESC, customer_id, LIMIT 5. La segunda columna de ORDER BY importa: los clientes 4, 7 y 11 tienen 4 pedidos cada uno, y hasta nueve clientes tienen 3. Sin ella, quién queda quinto no está prometido.
Míralo
Lo que SQLite perdona
Según la regla, un alias en WHERE no debería funcionar. Los productos que con un 20 % de descuento cuestan menos de 1 €:
SQLite lo acepta. También acepta un alias en GROUP BY:
Otras bases no son tan permisivas. PostgreSQL acepta un alias en GROUP BY, pero no en WHERE: allí dice que la columna sale no existe. SQL Server no lo acepta ni en WHERE ni en GROUP BY.
La permisividad de SQLite tiene límites. Si el alias se llama igual que una columna, WHERE toma la columna:
Salen dos productos, no cuatro: WHERE price < 1 mira el precio real, no el rebajado. Y un alias de una agregación en WHERE sigue siendo «misuse of aggregate», porque en el paso 2 todavía no hay grupos:
Una consulta que funciona en todas partes: en WHERE repite la expresión (WHERE price * 0.8 < 1), y usa los alias con libertad en ORDER BY.
Ejercicio
El informe mensual
La dueña quiere un informe de 2026: para cada mes, cuántos pedidos hay (sin los cancelados) y de cuántos clientes distintos. Los meses con menos de 3 pedidos son demasiado tranquilos y no los necesita.
Muestra las columnas month (año-mes), orders y customers de los meses de 2026 con al menos 3 pedidos, sin contar los cancelados, en el orden de los meses.
Solución
SELECT strftime('%Y-%m', ordered_at) AS month,
COUNT(*) AS orders,
COUNT(DISTINCT customer_id) AS customers
FROM orders
WHERE status <> 'cancelled' AND ordered_at >= '2026-01-01'
GROUP BY strftime('%Y-%m', ordered_at)
HAVING COUNT(*) >= 3
ORDER BY month;Cada parte está en su sitio:
WHERE status <> 'cancelled' AND ordered_at >= '2026-01-01': las filas;GROUP BY strftime('%Y-%m', ordered_at): los meses;HAVING COUNT(*) >= 3: los grupos;COUNT(*) AS orders, COUNT(DISTINCT customer_id) AS customers: las columnas;ORDER BY month: el orden.
Seis meses. En septiembre, 7 pedidos vienen de 6 clientes: uno de ellos pidió dos veces.
Test
Ponte a prueba
Lecciones relacionadas
Sección «Lecciones relacionadas»- Se basa en: Funciones de fecha · Funciones de agregación: COUNT, SUM, AVG · GROUP BY: un informe por grupos · HAVING: una condición para los grupos
- Continúa en: Funciones de ventana · La panadería, etapa 3: las preguntas
- Ejercicios sobre el tema: Resumir