Al terminar, podrás
- Elegir una subconsulta cuando solo necesitas obtener un valor o conjunto auxiliar en un punto concreto.
- Elegir una CTE cuando nombrar un resultado intermedio hace más fácil leer, comprobar o reutilizar una parte de la consulta.
- Combinar una CTE con una subconsulta sin cambiar la unidad de comparación.
- Probar cada etapa antes de montar la consulta completa.
Recupera lo aprendido: ¿Qué representa una fila antes y después de agrupar pedidos por cliente_id?
La idea clave: elige por claridad, no por apariencia
Una subconsulta y una CTE pueden resolver problemas parecidos. La pregunta útil no es cuál “es mejor” en general, sino qué forma hace más visible el razonamiento de esta consulta. Si necesitas un único valor, como un promedio, una subconsulta escalar puede ser suficiente. Si primero necesitas construir un conjunto con significado propio —por ejemplo, el gasto válido de cada cliente—, darle un nombre con WITH suele facilitar la lectura y la comprobación.
No uses CTE como promesa de rendimiento. PostgreSQL, MySQL y SQLite pueden aplicar estrategias distintas de optimización o materialización. Aquí la usamos para organizar el razonamiento; el rendimiento se mide con el motor y los datos reales.
Un mapa de decisión
¿Necesito un valor auxiliar en un solo punto?
→ Prueba una subconsulta.
¿Necesito nombrar y comprobar un conjunto intermedio?
→ Prueba una CTE.
¿El resultado intermedio se usa también para calcular otro valor?
→ Puede tener sentido combinar CTE + subconsulta.Ejemplo paso a paso
Queremos encontrar clientes cuyo gasto en pedidos no cancelados esté por encima del gasto medio de los clientes que sí tienen pedidos válidos. La unidad final es un cliente. Por eso conviene calcular primero una fila por cliente y solo después obtener el promedio de esas filas.
1. Comprueba el resultado intermedio
SELECT cliente_id,
ROUND(SUM(total), 2) AS total_valido
FROM pedidos
WHERE estado <> 'cancelado'
GROUP BY cliente_id
ORDER BY cliente_id;Esta consulta devuelve diez clientes. El promedio de sus totales válidos es 265,90. Ese es el valor que debe compararse con cada total por cliente.
2. Nombra ese conjunto y calcula el promedio sobre él
WITH totales_cliente AS (
SELECT cliente_id,
ROUND(SUM(total), 2) AS total_valido
FROM pedidos
WHERE estado <> 'cancelado'
GROUP BY cliente_id
)
SELECT cliente_id, total_valido
FROM totales_cliente
WHERE total_valido > (
SELECT AVG(total_valido)
FROM totales_cliente
)
ORDER BY total_valido DESC, cliente_id;| cliente_id | total_valido |
|---|---|
| 5 | 1112,00 |
| 1 | 377,40 |
| 8 | 268,00 |
Cómo comprobarla
- Ejecuta primero el
SELECTque agrupa pedidos y confirma que una fila representa un cliente. - Comprueba el promedio sobre esas diez filas: 265,90.
- Solo entonces aplica la comparación exterior. Deben quedar tres clientes.
- El
ORDER BYexterior fija un resultado determinista para poder revisarlo.
Un error habitual
Comparar unidades distintas
WITH totales_cliente AS (
SELECT cliente_id, SUM(total) AS total_valido
FROM pedidos
WHERE estado <> 'cancelado'
GROUP BY cliente_id
)
SELECT cliente_id, total_valido
FROM totales_cliente
WHERE total_valido > (SELECT AVG(total) FROM pedidos);La consulta funciona sintácticamente, pero responde otra pregunta: compara un total por cliente con el promedio de pedidos individuales. Antes de aceptar un resultado, escribe con palabras qué representa una fila a cada lado de la comparación.
Practica con datos
Laboratorio SQL
Clientes con gasto superior al promedio
Crea totales_cliente con cliente_id y la suma redondeada a dos decimales de los pedidos no cancelados como total_valido. Incluye solo clientes con pedidos no cancelados. Devuelve cliente_id y total_valido cuando este supere AVG(total_valido) de la misma CTE. Ordena por total_valido descendente y cliente_id ascendente.
Resultado esperado: 3 filas con cliente_id, total_valido, ordenadas de mayor a menor.
Ver tablas y datos
Cargando la estructura del ejercicio…
Dentro de la CTE filtra primero los pedidos con estado <> 'cancelado' y agrupa por cliente_id.
La subconsulta debe calcular AVG(total_valido) leyendo de totales_cliente, no de pedidos.
Compara total_valido con esa subconsulta y termina con ORDER BY total_valido DESC, cliente_id.
Solución
WITH totales_cliente AS (
SELECT cliente_id,
ROUND(SUM(total), 2) AS total_valido
FROM pedidos
WHERE estado <> 'cancelado'
GROUP BY cliente_id
)
SELECT cliente_id, total_valido
FROM totales_cliente
WHERE total_valido > (
SELECT AVG(total_valido)
FROM totales_cliente
)
ORDER BY total_valido DESC, cliente_id;La CTE fija la unidad en una fila por cliente. La subconsulta calcula el promedio sobre ese mismo conjunto y la consulta exterior conserva únicamente los totales superiores.
Puedes consultar la solución y volver a intentarlo. La práctica se completa cuando tu consulta funciona.
La consulta usa datos ficticios y se ejecuta solo en este navegador.
Ronda autónoma: cuándo basta una subconsulta
Pedidos válidos por encima de su promedio
Sin crear una CTE, devuelve id y total de los pedidos no cancelados cuyo total sea superior al promedio de los pedidos no cancelados. Ordena de mayor a menor total y por id como desempate.
total > (SELECT AVG(total) ...).Solución razonada
SELECT id, total
FROM pedidos
WHERE estado <> 'cancelado'
AND total > (
SELECT AVG(total)
FROM pedidos
WHERE estado <> 'cancelado'
)
ORDER BY total DESC, id;Aquí la subconsulta es suficiente porque solo necesitas un valor auxiliar —el promedio— en un punto de la consulta. Crear una CTE no aportaría una etapa intermedia que necesites nombrar o reutilizar.
Si te bloqueas, vuelve a la unidad de una fila
- Escribe qué representa una fila del resultado final.
- Ejecuta por separado la consulta que produce el conjunto intermedio.
- Comprueba cuántas filas y columnas devuelve la subconsulta.
- Solo después combina las piezas y verifica el recuento final.
Si el problema está en la forma de la subconsulta, repasa subconsultas. Si el problema es organizar etapas, vuelve a CTE con WITH.
Fuentes técnicas consultadas
Estas fuentes oficiales respaldan el alcance y la sintaxis utilizados en la práctica.
- PostgreSQL: consultas WITH y CTE
- PostgreSQL: expresiones con subconsultas
- SQLite: cláusula WITH
- MySQL 8.4: common table expressions
Comprueba si puedes explicarlo
En esta práctica, ¿el promedio incluye a los clientes sin pedidos válidos?
Ver respuesta razonada
No. La CTE parte de pedidos no cancelados: solo representa clientes con al menos uno. Incluir también clientes con total cero exigiría partir de clientes y conservarlos con LEFT JOIN.
Responde antes de abrir la explicación. Si necesitaste ayuda, cierra la respuesta y vuelve a explicarlo con tus palabras antes de dar la lección por aprendida.
Qué debes recordar
- Una subconsulta es una buena opción cuando necesitas un resultado auxiliar en un lugar concreto.
- Una CTE ayuda cuando un conjunto intermedio merece un nombre y conviene comprobarlo por separado.
- Si combinas ambas, compara valores que representen la misma unidad de análisis.
- La forma más legible no implica automáticamente mejor rendimiento: mídelo cuando el rendimiento importe.
A continuación: continúa con operaciones de conjuntos con UNION.