common-table-expressions-cte

Common Table Expressions (CTE) en SQL

  • 4 min

Una CTE (Common Table Expression) es un conjunto de resultados temporal y con nombre que se define y existe únicamente durante la ejecución de una sola consulta.

Imagina que has escrito una consulta con tres subconsultas anidadas dentro del FROM, dos en el WHERE y un JOIN a otra subconsulta.

Si intentas volver a leer o modificar ese script una semana después… probablemente no entiendas absolutamente nada de lo que hace.

Para solucionar este caos SQL Server introdujo las CTEs. Su principal propósito no es mejorar el rendimiento (que suele ser igual al de una subconsulta), sino mejorar la legibilidad y organización de nuestras consultas.

Piensa en una CTE como una tabla temporal con nombre que solo existe durante la ejecución de una única consulta.

Sintaxis básica: La cláusula WITH

Una CTE se define antes de tu consulta principal usando la palabra clave WITH.

WITH VentasTotales AS (
    -- Aquí va tu consulta compleja
    SELECT ClienteID, SUM(Total) as TotalGastado
    FROM Pedidos
    GROUP BY ClienteID
)

-- Aquí empieza la consulta real que usa la CTE
SELECT 
    C.Nombre,
    CTE.TotalGastado
FROM Clientes C

INNER JOIN VentasTotales CTE ON C.ClienteID = CTE.ClienteID

WHERE CTE.TotalGastado > 1000;
Copied!

Fíjate en el cambio de mentalidad:

  1. Definimos el conjunto de datos de “Ventas Totales”.
  2. Usamos ese conjunto limpio en la consulta final.

Encadenando CTEs

La verdadera gracia surge cuando defines múltiples CTEs, donde la segunda lee de la primera, la tercera de la segunda, etc. Esto nos permite programar de forma “procedimental” y lógica.

Imagina que queremos:

Obtener las ventas por empleado.

Calcular el promedio de ventas por empleado de la empresa.

Filtrar los empleados que vendieron más que el promedio.

Con subconsultas sería un infierno. Con CTEs es un paseo:

WITH VentasPorEmpleado AS (
    -- Paso 1: Datos crudos agrupados
    SELECT EmpleadoID, SUM(Importe) as Total
    FROM Ventas
    GROUP BY EmpleadoID
),

PromedioGlobal AS (
    -- Paso 2: Usamos el paso 1 para calcular el promedio
    SELECT AVG(Total) as MediaEmpresa
    FROM VentasPorEmpleado
)

-- Paso 3: Cruzamos ambos resultados
SELECT 
    V.EmpleadoID, 
    V.Total,
    P.MediaEmpresa
FROM VentasPorEmpleado V
CROSS JOIN PromedioGlobal P
WHERE V.Total > P.MediaEmpresa;
Copied!

El código se lee de arriba a abajo, como una receta de cocina.

CTEs Recursivas

Hasta aquí, las CTEs son “azúcar sintáctico” para ordenar código. Pero hay algo que las subconsultas normales no pueden hacer y las CTEs sí: llamarse a sí mismas.

Las CTEs Recursivas sirven para recorrer jerarquías (árboles genealógicos, organigramas de empresas, categorías de productos anidadas).

Estructura de una recursión

Se compone de dos partes unidas por un UNION ALL:

  1. Miembro ancla (Anchor): El punto de partida
  2. Miembro recursivo: La consulta que hace referencia a la propia CTE (NombreCTE) para buscar a los “hijos”.

Vamos a verlo con un ejemplo de un organigrama de una empresa,

Tenemos una tabla Empleados con ID y JefeID. Queremos listar a todos los empleados y saber en qué nivel de la jerarquía están.

Sería algo así

WITH Organigrama AS (
    -- 1. ANCLA: Buscamos al CEO (El que no tiene jefe o JefeID es NULL)
    SELECT EmpleadoID, Nombre, JefeID, 1 AS Nivel
    FROM Empleados
    WHERE JefeID IS NULL
    
    UNION ALL
    
    -- 2. RECURSIVO: Buscamos a los empleados cuyo Jefe está en el paso anterior
    SELECT 
        E.EmpleadoID, 
        E.Nombre, 
        E.JefeID, 
        O.Nivel + 1 -- Aumentamos el nivel
    FROM Empleados E
    INNER JOIN Organigrama O ON E.JefeID = O.EmpleadoID
)
-- Usamos el resultado final
SELECT * FROM Organigrama ORDER BY Nivel;
Copied!

¿Cómo funciona?

  1. SQL ejecuta el Ancla: Encuentra al CEO (Nivel 1).
  2. Ejecuta la parte recursiva usando al CEO como Organigrama: Encuentra a los directores (Nivel 2).
  3. Vuelve a ejecutar la parte recursiva usando a los directores: Encuentra a los gerentes (Nivel 3).
  4. Repite hasta que no encuentra a nadie más y se detiene.

CTE vs Tablas Temporales

¿Guardo los datos intermedios en una CTE o en una #TablaTemporal?

CaracterísticaCTETabla Temporal (#)
PersistenciaNinguna. Solo existe en la consulta.Dura hasta que cierras la sesión.
AlmacenamientoMemoria (generalmente).Disco (TempDB).
ÍndicesNo puedes crear índices en ella.Sí puedes crear índices.
ReutilizaciónSolo una vez.Puedes consultarla varias veces.
RendimientoBueno para conjuntos pequeños/medianos.Mejor para volúmenes masivos.