Módulo 5 · Consultas avanzadas

Organiza consultas por etapas con CTE

Da nombre a un resultado intermedio con WITH y úsalo como una tabla temporal dentro de una única sentencia.

26 minNivel IntermedioAntes necesitas: Subconsultas, GROUP BY y JOIN

0 de 45 lecciones

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

Estructura
WITH nombre_cte AS (
  SELECT ...
)
SELECT ...
FROM nombre_cte;
  1. WITH anuncia una o más CTE.
  2. nombre_cte describe las filas producidas por la consulta interior.
  3. AS (...) contiene el SELECT que prepara el resultado.
  4. La consulta principal utiliza la CTE en FROM o JOIN igual 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.

SQL
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;
Resultado esperado
nombregasto_total
Sara Gómez1112.00
Ana Torres377.40
Hugo Castro268.00
Luis Martín253.40
Marta Ruiz215.90

Cómo se construye la respuesta

  1. La CTE descarta los pedidos cancelados antes de sumar.
  2. GROUP BY cliente_id produce una fila por cliente con su gasto válido.
  3. La consulta principal enlaza ese resumen con clientes para mostrar nombres.
  4. El WHERE exterior filtra una columna ya calculada por la CTE.
  5. El ORDER BY exterior fija el orden observable del resultado final.
Comprueba tu comprensión

¿Durante cuánto tiempo puedes consultar gasto_por_cliente?

Un error habitual

Suponer que el orden interno llega al resultado final

Consulta frágil
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

Pendiente

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…

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 BY de la consulta exterior.

Comprueba tu dominio

Antes de avanzar, intenta explicar sin mirar el ejemplo:

  • qué filas contiene pedidos_validos;
  • por qué HAVING aparece después de GROUP BY;
  • qué ocurriría si ejecutaras otro SELECT separado intentando leer la CTE.