Cuando empezás a escribir SQL, casi todo se resuelve con SELECT … FROM … WHERE …. Pero muy pronto aparecen las subconsultas: consultas dentro de otra consulta. Son poderosas… y si no las escribís con alias claros, se vuelven difíciles de leer, de depurar y de mantener.
En esta guía vas a aprender, desde cero y paso a paso, cómo usar alias en subconsultas para que tu SQL sea claro, profesional y fácil de entender. Veremos subconsultas en FROM, en WHERE con IN/EXISTS, subconsultas escalares en la lista SELECT, la relación con CTE (WITH) y un set de buenas prácticas de nombres. Incluyo tablas de resultados simplificadas para que se entienda qué devuelve cada ejemplo.
1) ¿Qué es una subconsulta? (y por qué los alias importan)
Una subconsulta es una consulta anidada dentro de otra. Puede aparecer en varias partes:
- En
FROM(también llamada tabla derivada o inline view). - En
WHERE(muy común conINoEXISTS). - En la lista
SELECT(subconsulta escalar: devuelve un solo valor por fila).
¿Por qué usar alias?
Porque una subconsulta, especialmente en FROM, se comporta como una tabla. Igual que cualquier tabla, necesita un nombre para referirse a sus columnas desde afuera. Además, los alias:
- Aclaran qué hace cada bloque (ej.:
ventas_30d,agg,top_por_categoria). - Evitan ambigüedad cuando hay varias tablas con columnas homónimas (
id,fecha,total). - Ayudan a dividir el problema en piezas que se entienden de un vistazo.
2) Subconsulta en FROM: alias obligatorio
Imaginá dos tablas:
clientes(id, nombre)pedidos(id, cliente_id, fecha, total)
Queremos el total de ventas por cliente en los últimos 30 días. Una forma clásica es calcular primero un agregado por cliente en una subconsulta y ponerle un alias.
2.1 Versión correcta (con alias de subconsulta y alias de columnas)
SELECT
c.nombre AS cliente,
agg.pedidos AS cantidad_pedidos,
agg.ventas AS ventas_30d
FROM clientes AS c
JOIN (
SELECT
p.cliente_id,
COUNT(*) AS pedidos,
SUM(p.total) AS ventas
FROM pedidos AS p
WHERE p.fecha >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY p.cliente_id
) AS agg
ON agg.cliente_id = c.id
ORDER BY agg.ventas DESC;
- La subconsulta recibe el alias
agg(de aggregate). - Dentro, las columnas tienen alias semánticos:
pedidos,ventas. - Afuera, usamos esos nombres para producir una salida entendible.
Resultado (simplificado):
| cliente | cantidad_pedidos | ventas_30d |
|---|---|---|
| Elsa | 4 | 900.00 |
| Carla | 2 | 720.00 |
| Ana | 2 | 440.00 |
| … | … | … |
2.2 ¿Qué pasa si no le ponés alias?
SELECT c.nombre, t.ventas
FROM clientes c
JOIN (SELECT cliente_id, SUM(total) AS ventas FROM pedidos GROUP BY cliente_id) -- ❌ falta alias aquí
ON t.cliente_id = c.id;
Falla. Las bases exigen alias cuando hay una subconsulta en FROM. El parser necesita un nombre para ese bloque.
3) Alias dentro de la subconsulta vs fuera de la subconsulta
- Dentro de la subconsulta: alias para tablas internas (
p) y para columnas calculadas (ventas,pedidos). - Fuera de la subconsulta: alias del “bloque” completo (
agg) para poder referenciar sus columnas (agg.ventas).
Esto hace que cada capa sea autoexplicativa. Un buen patrón de nombres:
basepara datos sin procesar,aggpara agregaciones,top_…para rankings,resumen_…,ventas_30d,por_ciudad, etc.
4) Subconsulta en WHERE con IN: alias que ordenan la lógica
Supongamos que queremos todos los clientes que compraron algo caro (por ejemplo, pedidos con total > 300). Podemos filtrar por id usando una subconsulta con IN.
SELECT
c.id AS cliente_id,
c.nombre AS cliente
FROM clientes AS c
WHERE c.id IN (
SELECT p.cliente_id
FROM pedidos AS p
WHERE p.total > 300
);
- El alias
porganiza la subconsulta y la hace legible. - El resultado lista clientes que cumplieron esa condición al menos una vez.
Resultado (ejemplo):
| cliente_id | cliente |
|---|---|
| 3 | Carla |
| 5 | Elsa |
Nota: la subconsulta en
WHEREno requiere un alias para todo el bloque (como enFROM), pero sí conviene usar alias para las tablas dentro de esa subconsulta.
5) Subconsulta correlacionada con EXISTS: alias para entender el “diálogo” entre consultas
EXISTS verifica si la subconsulta devuelve al menos una fila. Lo común es que la subconsulta use la fila actual de la consulta externa (esto se llama correlación).
SELECT
c.id,
c.nombre
FROM clientes AS c
WHERE EXISTS (
SELECT 1
FROM pedidos AS p
WHERE p.cliente_id = c.id -- ← correlación
AND p.fecha >= CURRENT_DATE - INTERVAL '30 days'
);
c(afuera) yp(adentro) hacen visible la correlaciónp.cliente_id = c.id.- Con alias, se lee como una frase: “clientes c para los que existen pedidos p en los últimos 30 días”.
¿Cuándo usar EXISTS vs IN?
INes simple cuando comparás una sola columna y no hay duplicados problemáticos.EXISTSsuele ser más claro (y a veces más eficiente) cuando la lógica depende de múltiples condiciones o uniones dentro de la subconsulta.
6) Subconsultas escalares en la lista SELECT (un valor por fila)
Una subconsulta escalar es la que devuelve un único valor para cada fila externa. Por ejemplo, queremos, para cada cliente, la fecha de su último pedido:
SELECT
c.id AS cliente_id,
c.nombre AS cliente,
(
SELECT MAX(p.fecha)
FROM pedidos AS p
WHERE p.cliente_id = c.id
) AS ultima_compra
FROM clientes AS c
ORDER BY ultima_compra DESC NULLS LAST;
- El alias
ultima_compraexplica el significado del valor. - Si un cliente no tiene pedidos, el valor será NULL.
Resultado (ejemplo):
| cliente_id | cliente | ultima_compra |
|---|---|---|
| 5 | Elsa | 2024-06-20 |
| 3 | Carla | 2024-06-12 |
| 8 | Hugo | (NULL) |
Diferencia con
JOIN: una subconsulta escalar no duplica filas de la tabla externa; en cambio, trae un solo dato calculado para cada fila.
7) De subconsulta a CTE (WITH): ¿cuándo conviene?
A veces la subconsulta de FROM crece y crece. Para que el SQL sea más fácil de leer y reutilizar, conviene subirla a un CTE (expresión común de tabla).
7.1 Con subconsulta en FROM
SELECT c.nombre, agg.ventas
FROM clientes c
JOIN (
SELECT cliente_id, SUM(total) AS ventas
FROM pedidos
WHERE fecha >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY cliente_id
) AS agg ON agg.cliente_id = c.id
ORDER BY agg.ventas DESC;
7.2 La misma lógica con CTE (más legible)
WITH ventas_30d AS (
SELECT cliente_id, SUM(total) AS ventas
FROM pedidos
WHERE fecha >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY cliente_id
)
SELECT
c.nombre AS cliente,
v.ventas AS ventas_30d
FROM clientes AS c
JOIN ventas_30d AS v ON v.cliente_id = c.id
ORDER BY v.ventas DESC;
- El alias del CTE es el nombre del bloque (
ventas_30d). - Luego se usa como si fuera una tabla (
v), con sus alias de columnas (ventas).
Cuándo usar CTE:
- La subconsulta se usa más de una vez.
- La consulta necesita etapas (base → agregado → ranking).
- Quieres comentar, testear o leer cada pieza por separado.
8) Buenas prácticas de alias específicas para subconsultas
- Nombres que cuentan una historia:
ventas_30d,agg_clientes,top_por_categoria,resumen_mensual. - Evitar
t1,sub,tmpsalvo en demos mínimos. - Prefijos por propósito:
agg_(agregaciones),dim_(dimensiones),fact_(hechos),rnk_(rankings).
- Alias cortos para tablas internas:
c(clientes),p(pedidos),e(empleados). - Alias de columnas semánticos:
ventas,pedidos,ticket_promedio,ultima_compra. - Consistencia de estilo:
snake_caseen minúsculas (ventas_ciudad), sin acentos y evitando palabras reservadas.
9) Errores comunes (y cómo corregirlos)
- Olvidar el alias de la subconsulta en
FROM- Síntoma: error de sintaxis.
- Solución:
… FROM (subconsulta) AS nombre_bloque.
- Alias duplicados o confusos
- Síntoma: columnas o bloques que se pisan o no se entiende de dónde vienen.
- Solución: alias únicos y descriptivos.
- Usar alias de
SELECTenWHERE- Motivo: orden lógico;
WHEREse evalúa antes queSELECT. - Solución: repetí la expresión o subila a subconsulta/CTE.
- Motivo: orden lógico;
- Ambigüedad de columna (mismo nombre en varias tablas)
- Síntoma: “column reference is ambiguous”.
- Solución: calificar con alias de tabla:
c.id,p.id.
- Subconsultas escalares que devuelven >1 fila
- Síntoma: error “more than one row returned”.
- Solución: agregá
MAX,MIN,LIMIT 1/TOP 1/FETCH FIRST 1 ROW ONLYsegún motor.
- Rendimiento (subconsultas correlacionadas pesadas)
- Solución: mover a CTE o a
JOIN+ agregación si corresponde, y crear índices adecuados (cliente_id,fecha).
- Solución: mover a CTE o a
10) Tres casos completos con subconsultas + alias
10.1 “Clientes activos con gasto y última compra” (subconsulta en FROM + escalar)
SELECT
c.id AS cliente_id,
c.nombre AS cliente,
agg.ventas AS ventas_30d,
(
SELECT MAX(p.fecha)
FROM pedidos AS p
WHERE p.cliente_id = c.id
) AS ultima_compra
FROM clientes AS c
JOIN (
SELECT
p.cliente_id,
SUM(p.total) AS ventas
FROM pedidos AS p
WHERE p.fecha >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY p.cliente_id
) AS agg ON agg.cliente_id = c.id
WHERE c.activo = TRUE
ORDER BY agg.ventas DESC;
Salida (ejemplo):
| cliente_id | cliente | ventas_30d | ultima_compra |
|---|---|---|---|
| 5 | Elsa | 900.00 | 2024-06-20 |
| 3 | Carla | 720.00 | 2024-06-12 |
10.2 “Top 3 productos por categoría” (subconsulta correlacionada)
Tablas: productos(id, nombre, categoria, precio) y ventas(id, producto_id, unidades).
SELECT
pr.categoria,
pr.nombre AS producto,
SUM(v.unidades) AS unidades
FROM productos AS pr
JOIN ventas AS v ON v.producto_id = pr.id
GROUP BY pr.categoria, pr.nombre
HAVING
SUM(v.unidades) >= (
SELECT
MIN(t3.unidades)
FROM (
SELECT
p2.nombre,
SUM(v2.unidades) AS unidades
FROM productos p2
JOIN ventas v2 ON v2.producto_id = p2.id
WHERE p2.categoria = pr.categoria -- ← correlación por categoría
GROUP BY p2.nombre
ORDER BY unidades DESC
FETCH FIRST 3 ROWS ONLY -- TOP 3 por categoría (PostgreSQL/Oracle). En MySQL usar LIMIT 3.
) AS t3
)
ORDER BY pr.categoria, unidades DESC;
t3es una subconsulta que calcula las top 3 unidades por categoría; usamos su mínimo para filtrar los que entran.- Alias claros explican cada nivel (
t3,p2,v2).
10.3 “Último pedido por cliente con detalle” (subconsulta + join para traer columnas auxiliares)
-- Subconsulta: por cliente, su última fecha de pedido
SELECT
c.nombre AS cliente,
p2.fecha AS fecha_ultima,
p2.total AS total_ultimo
FROM clientes AS c
JOIN (
SELECT p.cliente_id, MAX(p.fecha) AS fecha_ultima
FROM pedidos AS p
GROUP BY p.cliente_id
) AS ult ON ult.cliente_id = c.id
JOIN pedidos AS p2
ON p2.cliente_id = c.id
AND p2.fecha = ult.fecha_ultima
ORDER BY p2.fecha DESC;
ultobtiene la última fecha por cliente.- Luego unimos con
pedidos p2para traer total,iddel pedido, etc.
Resultado (ejemplo):
| cliente | fecha_ultima | total_ultimo |
|---|---|---|
| Elsa | 2024-06-20 | 320.00 |
| Carla | 2024-06-12 | 220.00 |
11) Diferencias entre motores (para escribir subconsultas portables)
- Límites:
- PostgreSQL / Oracle:
FETCH FIRST 1 ROW ONLYoLIMIT 1(PostgreSQL). - MySQL / MariaDB:
LIMIT 1. - SQL Server:
TOP 1al principio delSELECT.
- PostgreSQL / Oracle:
- Fechas:
- PostgreSQL/Oracle:
CURRENT_DATE - INTERVAL '30 days'. - MySQL:
CURDATE() - INTERVAL 30 DAY. - SQL Server:
DATEADD(DAY, -30, CAST(GETDATE() AS DATE)).
- PostgreSQL/Oracle:
- Comillas para alias con espacios:
- Estándar/PG/Oracle:
"Nombre completo". - SQL Server:
[Nombre completo]o"Nombre completo". - MySQL:
"Nombre completo"(segúnsql_mode) o`Nombre completo`.
- Estándar/PG/Oracle:
Sugerencia universal:
- Evitá espacios y acentos en alias; preferí
snake_case. - Usá
AS(aunque sea opcional) por claridad. - Elegí alias semánticos (de negocio).
12) Checklist rápido antes de publicar/compartir tu consulta
- ¿Cada subconsulta en
FROMtiene alias (ej.:agg,ventas_30d)? - ¿Las tablas internas dentro de la subconsulta tienen alias cortos (
c,p)? - ¿Las columnas calculadas tienen nombres de negocio (
ventas,pedidos,ultima_compra)? - ¿Evitaste palabras reservadas y alias duplicados?
- ¿Tu subconsulta escalar garantiza devolver una sola fila por cada fila externa?
- ¿Consideraste mover subconsultas grandes a CTE para legibilidad/reutilización?
Cierre
Las subconsultas permiten resolver problemas complejos con SQL sin perder claridad, siempre que les des alias correctos. Pensá cada subconsulta como una pieza con nombre y propósito: “esta calcula ventas_30d”, “esta trae el top por categoría”, “esta obtiene la última compra”. Con alias descriptivos y consistentes, tu SQL se vuelve legible, mantenible y apto para colaborar en equipo —sin sustos cuando vuelvas al código dentro de seis meses.
📚 Artículos relacionados
- Cómo usar alias en SQL para simplificar consultas SELECT con ejemplos
Guía paso a paso para principiantes donde aprenderás a usar alias en combinación con SELECT, con ejemplos prácticos y resultados claros. - Buenas prácticas para nombrar columnas y tablas con alias en SQL
Recomendaciones claras y ejemplos detallados para elegir alias descriptivos, consistentes y fáciles de entender en tus consultas SQL. - Uso de alias en SQL con funciones de agregación como COUNT y SUM
Ejemplos prácticos y explicaciones para aplicar alias en funciones de agregación y generar reportes legibles y profesionales. - Errores comunes al trabajar con alias en SQL y cómo evitarlos
Lista de errores frecuentes al usar alias en SQL con soluciones claras para evitarlos y escribir consultas sin fallos.
