Al terminar, podrás
- Definir una CTE ordinaria con
WITH nombre AS (...). - Leer una consulta compleja como una secuencia de resultados intermedios con nombre.
- Entender que una CTE existe solo durante la sentencia que la declara y que el orden final requiere su propio
ORDER BY.
Activa lo que ya sabes: una CTE no introduce una forma nueva de obtener filas. Dentro de sus paréntesis escribes un SELECT como los que ya practicaste. La novedad es que asignas un nombre a ese resultado para consultarlo después.
Recupera lo aprendido: ¿Qué forma necesita una subconsulta utilizada a la derecha de precio >: un valor o una tabla con varias columnas?
La idea clave
Una expresión de tabla común, o CTE por sus siglas en inglés, es un resultado con nombre que existe únicamente durante la sentencia que lo declara. Puedes imaginarla como una mesa de trabajo: primero preparas un conjunto pequeño y comprensible; después construyes la respuesta final a partir de él.
Para aprender, la ventaja principal es la claridad: en lugar de esconder un resultado dentro de muchos paréntesis, le das un nombre y lees la consulta por etapas: “primero prepara estas filas; después usa ese resultado”.
Rendimiento: usa una CTE para organizar el razonamiento, no como promesa de velocidad. Cada motor puede optimizarla de forma distinta; cuando el rendimiento importe, mídelo.
La sintaxis básica
WITH nombre_cte AS (
SELECT ...
)
SELECT ...
FROM nombre_cte;WITHanuncia una o más CTE.nombre_ctedescribe las filas producidas por la consulta interior.AS (...)contiene elSELECTque prepara el resultado.- La consulta principal utiliza la CTE en
FROMoJOINigual que usaría una tabla.
Ejemplo paso a paso
Queremos encontrar clientes cuyo gasto válido —pedidos no cancelados— alcance al menos 200. La CTE resume primero el gasto por cliente; la consulta exterior añade el nombre y filtra el resumen.
WITH gasto_por_cliente AS (
SELECT cliente_id,
ROUND(SUM(total), 2) AS gasto_total
FROM pedidos
WHERE estado <> 'cancelado'
GROUP BY cliente_id
)
SELECT c.nombre,
g.gasto_total
FROM gasto_por_cliente AS g
JOIN clientes AS c ON c.id = g.cliente_id
WHERE g.gasto_total >= 200
ORDER BY g.gasto_total DESC, c.nombre;| nombre | gasto_total |
|---|---|
| Sara Gómez | 1112.00 |
| Ana Torres | 377.40 |
| Hugo Castro | 268.00 |
| Luis Martín | 253.40 |
| Marta Ruiz | 215.90 |
Cómo se construye la respuesta
- La CTE descarta los pedidos cancelados antes de sumar.
GROUP BY cliente_idproduce una fila por cliente con su gasto válido.- La consulta principal enlaza ese resumen con
clientespara mostrar nombres. - El
WHEREexterior filtra una columna ya calculada por la CTE. - El
ORDER BYexterior fija el orden observable del resultado final.
Un error habitual
Suponer que el orden interno llega al resultado final
WITH pedidos_recientes AS (
SELECT id, fecha, total
FROM pedidos
ORDER BY fecha DESC
)
SELECT id, fecha, total
FROM pedidos_recientes;Una tabla o CTE representa un conjunto sin orden garantizado. Aunque un motor acepte el ORDER BY interior, la consulta exterior puede devolver las filas en otro orden. Coloca el ORDER BY que necesita el alumno o la aplicación en el SELECT final.
Practica con datos
Laboratorio SQL
Resume pedidos válidos por cliente
Crea una CTE llamada pedidos_validos con cliente_id y total de pedidos no cancelados. Agrupa por cliente_id y devuelve cliente_id y la suma redondeada a dos decimales como total_valido. Conserva sumas de al menos 200 y ordena por total_valido descendente y cliente_id ascendente como desempate.
Resultado esperado: 5 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 con estado <> 'cancelado'.
La consulta exterior agrupa por cliente_id y usa SUM(total).
Filtra los grupos con HAVING SUM(total) >= 200 y ordena por el alias descendente.
Solución
WITH pedidos_validos AS (
SELECT cliente_id, total
FROM pedidos
WHERE estado <> 'cancelado'
)
SELECT cliente_id,
ROUND(SUM(total), 2) AS total_valido
FROM pedidos_validos
GROUP BY cliente_id
HAVING SUM(total) >= 200
ORDER BY total_valido DESC, cliente_id;La CTE deja una entrada limpia para la agregación. La consulta exterior decide la forma final: agrupa, filtra los grupos y ordena el resultado.
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.
Si todavía se mezcla con las subconsultas
Empieza por una pregunta simple: “¿necesito poner el resultado dentro de una expresión o quiero darle un nombre y seguir trabajando con sus filas?”. Una subconsulta escalar encaja en comparaciones como precio > (SELECT AVG(precio) ...). Una CTE encaja cuando el resultado intermedio tiene varias columnas, será unido con otra tabla o mejora la lectura por etapas. Repasa subconsultas y vuelve a transformar uno de sus ejemplos en una CTE.
Compatibilidad y límites del laboratorio
La forma ordinaria WITH nombre AS (SELECT ...) está documentada en PostgreSQL, MySQL 8.0 y SQLite. Los tres motores también ofrecen CTE recursivas, pero su sintaxis, límites y optimización requieren una lección aparte. El laboratorio de AprenderSQL.es ejecuta CTE ordinarias de lectura; no simula recursión ni operaciones de escritura dentro de WITH.
Fuentes técnicas consultadas
Comprueba si puedes explicarlo
Tras ejecutar WITH resumen AS (...) SELECT ... FROM resumen;, ¿puedes consultar resumen en una segunda sentencia?
Ver respuesta razonada
No. El nombre solo existe dentro de la sentencia que declara WITH. Repite la CTE en la nueva sentencia o, cuando lo necesites, define una vista reutilizable.
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 CTE es un resultado con nombre y alcance de una sola sentencia.
- La consulta interior prepara filas; la consulta exterior construye la respuesta final.
- El nombre debe explicar qué contiene el resultado, no usar etiquetas vagas como
datos1. - Una CTE mejora la estructura, pero no garantiza por sí sola mejor rendimiento.
- El orden final solo queda definido por el
ORDER BYde la consulta exterior.
Comprueba tu dominio
Antes de avanzar, intenta explicar sin mirar el ejemplo:
- qué filas contiene
pedidos_validos; - por qué
HAVINGaparece después deGROUP BY; - qué ocurriría si ejecutaras otro
SELECTseparado intentando leer la CTE.