🧚 ¡Oye! Beekeeper Studio es una GUI de bases de datos rápida, moderna y de código abierto Descarga
7 de enero de 2026 Por Lucas Gray

Una subconsulta es una consulta anidada dentro de otra. Un CTE (common table expression, o expresión de tabla común) es esa misma consulta interna movida al inicio de la sentencia con WITH y con un nombre, para que una consulta larga se lea como una serie de pasos. Los ejemplos funcionan en PostgreSQL, MySQL, SQLite y SQL Server.

Cómo usar CTEs y subconsultas

Una subconsulta va donde se usa: en FROM actúa como una tabla, y en WHERE o SELECT aporta un valor o una lista. Un CTE se define primero y luego la consulta que sigue lo usa como si fuera una tabla:

-- Subquery: written where it's used
SELECT columns FROM (SELECT ...) AS name;

-- CTE: named first, then used like a table
WITH name AS (SELECT ...)
SELECT columns FROM name;
SELECT n * 2 AS doubled FROM (SELECT 21 AS n) AS numbers;
-- Output: 42

WITH numbers AS (SELECT 21 AS n)
SELECT n * 2 AS doubled FROM numbers;
-- Output: 42

En una tabla real, las dos formas devuelven las mismas filas. Aquí tienes un mismo filtro escrito de ambas maneras:

-- Input: people
-- first_name | last_name | job      | favorite_food
-- -----------+-----------+----------+--------------
-- Matthew    | Ortiz     | Chef     | cheese
-- Matt       | Novak     | Pilot    | pizza
-- Mattie     | Brooks    | Engineer | cheese
-- Priya      | Nair      | Teacher  | cheese

-- Subquery
SELECT *
FROM (SELECT first_name, last_name, job FROM people WHERE favorite_food = 'cheese') AS cheese_lovers
WHERE first_name LIKE 'Matt%';

-- CTE
WITH cheese_lovers AS (
  SELECT first_name, last_name, job FROM people WHERE favorite_food = 'cheese'
)
SELECT * FROM cheese_lovers
WHERE first_name LIKE 'Matt%';

-- Output of both:
-- first_name | last_name | job
-- -----------+-----------+---------
-- Matthew    | Ortiz     | Chef
-- Mattie     | Brooks    | Engineer

Reglas que debes conocer

  • Un CTE solo dura una sentencia. La consulta justo después de WITH puede usarlo; la siguiente sentencia no. Para conservar un resultado en varias consultas, usa una tabla temporal o una vista.
  • Ponle nombre a una subconsulta en FROM, como AS numbers arriba. MySQL y SQL Server la rechazan si no lo tiene.
  • Una subconsulta usada como un solo valor debe devolver una fila:
SELECT (SELECT 1 UNION ALL SELECT 2) AS n;
-- ERROR: more than one row returned by a subquery used as an expression
-- (SQLite doesn't complain: it quietly uses the first row, 1)
  • La velocidad suele ser la misma. En general, las bases de datos planifican un CTE que se usa una sola vez igual que la subconsulta equivalente, así que elige la que se lea mejor.

Sintaxis alternativa

Un CTE que se refiere a sí mismo (ver abajo) se escribe WITH RECURSIVE:

-- In SQL Server, leave out RECURSIVE
WITH RECURSIVE counter AS (
  SELECT 1 AS n
  UNION ALL
  SELECT n + 1 FROM counter WHERE n < 3
)
SELECT n FROM counter;

-- Output:
-- n
-- -
-- 1
-- 2
-- 3

En SQL Server, la sentencia anterior a un WITH debe terminar con punto y coma, por eso el código T-SQL suele iniciar un CTE con ;WITH.

¿Cuándo usar un CTE en lugar de una subconsulta?

Una subconsulta está bien para un paso pequeño. Cámbiate a un CTE cuando la consulta tenga varios pasos, necesite el mismo resultado dos veces o tenga que referirse a sí misma.

Filtrar contra un valor calculado

Cuando la consulta interna solo produce un valor o una lista para comparar, como el total promedio de los pedidos, una subconsulta en WHERE es la forma más corta de escribirla. Un CTE también funcionaría, pero agrega un nombre que nadie necesita.

Dividir una consulta larga en pasos con nombre

Cuando una consulta tiene etapas (total por cliente, luego los primeros, luego la lista final), ponle nombre a cada etapa. Lista varios CTEs después de un solo WITH, separados por comas: cada uno puede usar los anteriores, y la consulta se lee de arriba hacia abajo en lugar de adentro hacia afuera.

Usar el mismo resultado más de una vez

Una sentencia puede referirse a un CTE todas las veces que quiera, así que un resultado que necesitas dos veces (una lista de totales y su promedio, o cada total y el total general) se escribe una sola vez. Con subconsultas tendrías que copiar toda la consulta interna.

Recorrer una jerarquía

Solo un CTE puede referirse a sí mismo. Un CTE recursivo parte de algunas filas y sigue uniéndose a la tabla hasta que no aparecen filas nuevas; así recorres un organigrama o un árbol de categorías, o generas una serie de números o fechas.

Ejemplos reales de CTEs y subconsultas

Productos con precio superior al promedio de su categoría

La subconsulta se refiere a p.category de la consulta externa, así que se calcula para la categoría de cada producto (una subconsulta correlacionada):

-- Input: products
-- name          | category  | price
-- --------------+-----------+-------
-- Desk Lamp     | Lighting  | 24.99
-- Floor Lamp    | Lighting  | 89.00
-- Office Chair  | Furniture | 149.00
-- Standing Desk | Furniture | 399.00
-- Bookshelf     | Furniture | 120.00

SELECT name, category, price
FROM products p
WHERE price > (
  SELECT AVG(price) FROM products WHERE category = p.category
)
ORDER BY category, name;

-- Output:
-- name          | category  | price
-- --------------+-----------+-------
-- Standing Desk | Furniture | 399.00
-- Floor Lamp    | Lighting  | 89.00

El producto más vendido de cada categoría

Tres pasos, cada uno con nombre: suma las unidades por producto, clasifica los productos dentro de cada categoría y luego quédate con el primero:

-- Input: sales
-- id | product      | category  | units
-- ---+--------------+-----------+------
-- 1  | Desk Lamp    | Lighting  | 3
-- 2  | Floor Lamp   | Lighting  | 5
-- 3  | Desk Lamp    | Lighting  | 4
-- 4  | Office Chair | Furniture | 2
-- 5  | Bookshelf    | Furniture | 6

WITH product_totals AS (
  SELECT category, product, SUM(units) AS units
  FROM sales
  GROUP BY category, product
),
ranked AS (
  SELECT category, product, units,
         RANK() OVER (PARTITION BY category ORDER BY units DESC) AS place
  FROM product_totals
)
SELECT category, product, units
FROM ranked
WHERE place = 1
ORDER BY category;

-- Output:
-- category  | product   | units
-- ----------+-----------+------
-- Furniture | Bookshelf | 6
-- Lighting  | Desk Lamp | 7

La participación de cada región en las ventas

region_totals se usa dos veces: una para las filas y otra dentro de una subconsulta para el total general:

-- Input: orders
-- id | region | amount
-- ---+--------+--------
-- 1  | North  | 1200.00
-- 2  | South  | 450.00
-- 3  | North  | 300.00
-- 4  | West   | 800.00
-- 5  | South  | 250.00

WITH region_totals AS (
  SELECT region, SUM(amount) AS total
  FROM orders
  GROUP BY region
)
SELECT
  region,
  total,
  ROUND(100.0 * total / (SELECT SUM(total) FROM region_totals), 1) AS pct_of_sales
FROM region_totals
ORDER BY total DESC;

-- Output:
-- region | total   | pct_of_sales
-- -------+---------+-------------
-- North  | 1500.00 | 50.0
-- West   | 800.00  | 26.7
-- South  | 700.00  | 23.3

Escrita con subconsultas, la consulta con GROUP BY aparecería dos veces.

La cadena de jefes de un empleado

Empieza desde un empleado y sigue manager_id hacia arriba hasta que se acabe:

-- Input: employees
-- id | name      | manager_id
-- ---+-----------+-----------
-- 1  | Maya Chen | NULL
-- 2  | Sam Ortiz | 1
-- 3  | Jo Kim    | 2
-- 4  | Lee Ross  | 2
-- 5  | Ana Diaz  | 1

-- In SQL Server, leave out RECURSIVE
WITH RECURSIVE chain AS (
  SELECT id, name, manager_id, 0 AS level
  FROM employees
  WHERE name = 'Jo Kim'
  UNION ALL
  SELECT e.id, e.name, e.manager_id, chain.level + 1
  FROM employees e
  JOIN chain ON e.id = chain.manager_id
)
SELECT level, name FROM chain ORDER BY level;

-- Output:
-- level | name
-- ------+----------
-- 0     | Jo Kim
-- 1     | Sam Ortiz
-- 2     | Maya Chen

Más lecturas sobre CTEs y subconsultas

En este blog:

Documentación oficial:

Beekeeper Studio es un cliente de bases de datos gratuito y de código abierto

La mejor herramienta de consultas y edición de SQL que he usado. Tiene todo lo que necesito para administrar mi base de datos. - ⭐⭐⭐⭐⭐ Mit

Beekeeper Studio es rápido, intuitivo y fácil de usar. Es compatible con muchísimas bases de datos y funciona de maravilla en Windows, Mac y Linux.

La versión de Beekeeper para Linux tiene el 100% de las funciones, sin recortes ni concesiones.

Lo que dicen los usuarios de Beekeeper Studio

★★★★★
"Beekeeper Studio reemplazó por completo mi antiguo flujo de trabajo con SQL. Es rápido, intuitivo y hace que trabajar con bases de datos vuelva a ser agradable."
— Alex K., desarrollador de bases de datos
★★★★★
"He probado muchas GUI de bases de datos, pero Beekeeper logra el equilibrio perfecto entre funciones y simplicidad. Simplemente funciona."
— Sarah M., ingeniera full stack

¿Listo para mejorar tu flujo de trabajo con SQL?

download Descarga gratis