En SQL, la cláusula FROM indica de qué tabla(s) o conjunto(s) de resultados se obtendrán los datos para una consulta. Es el punto de partida lógico del plan de ejecución: primero se construye el conjunto de filas base (tablas, vistas, subconsultas), luego se filtra (WHERE), se agrupa (GROUP BY), se filtra por agregados (HAVING), se proyecta (SELECT) y finalmente se ordena/limita (ORDER BY, LIMIT).
Aunque FROM parezca trivial, dominarlo es clave para escribir consultas claras, performantes y correctas, especialmente cuando hay múltiples tablas, subconsultas derivadas o funciones de ventana.
Sintaxis básica
SELECT columnas
FROM fuente
[WHERE ...]
[GROUP BY ...]
[HAVING ...]
[ORDER BY ...]
[LIMIT ...];
Donde fuente puede ser:
- Una tabla o vista:
FROM clientes - Varias tablas con JOIN:
FROM clientes c JOIN pedidos p ON p.cliente_id = c.id - Una subconsulta (tabla derivada):
FROM ( SELECT cliente_id, SUM(total) total FROM pedidos GROUP BY cliente_id ) t - Una CTE previa con
WITH:WITH ventas AS (...) SELECT ... FROM ventas; - Funciones o tablas especiales según el motor (ej.
generate_series()en PostgreSQL).
Ejemplos esenciales de FROM
1) Una sola tabla
SELECT id, nombre, email
FROM clientes;
2) Alias de tabla (recomendado)
SELECT c.id, c.nombre
FROM clientes AS c;
Usar alias corta la escritura y evita ambigüedades en joins.
3) INNER JOIN (coincidencias en ambas)
SELECT c.nombre, p.id, p.total
FROM clientes c
JOIN pedidos p ON p.cliente_id = c.id;
4) LEFT JOIN (todas las filas de la izquierda)
SELECT c.nombre, p.id AS pedido_id, p.total
FROM clientes c
LEFT JOIN pedidos p ON p.cliente_id = c.id;
Si un cliente no tiene pedidos, igual aparece con pedido_id = NULL.
5) FULL OUTER JOIN (si el motor lo soporta)
SELECT *
FROM a
FULL OUTER JOIN b ON a.id = b.id;
Devuelve coincidencias y no coincidencias de ambos lados.
6) CROSS JOIN (producto cartesiano)
SELECT a.x, b.y
FROM a
CROSS JOIN b;
Combina todas las filas de a con todas las de b. Úsalo con cuidado.
7) Subconsulta en FROM (tabla derivada)
SELECT t.cliente_id, t.total
FROM (
SELECT cliente_id, SUM(total) AS total
FROM pedidos
GROUP BY cliente_id
) AS t
WHERE t.total > 1000;
Útil para encapsular cálculos y reutilizarlos.
8) FROM con CTE (WITH)
WITH ventas_por_cliente AS (
SELECT cliente_id, SUM(total) total
FROM pedidos
GROUP BY cliente_id
)
SELECT c.nombre, v.total
FROM clientes c
JOIN ventas_por_cliente v ON v.cliente_id = c.id
WHERE v.total > 1000;
Más legible que una subconsulta anidada en FROM.
Orden lógico de evaluación (muy importante)
Aunque escribas SELECT ... FROM ... WHERE ..., el optimizador evalúa lógicamente así:
FROM(+JOIN): construye el conjunto base de filas.WHERE: filtra filas (antes de agregaciones).GROUP BY: agrupa.HAVING: filtra grupos.SELECT: proyecta columnas y expresiones.ORDER BY: ordena.LIMIT/OFFSET: limita.
Comprender esto evita errores como pretender usar un alias del SELECT en WHERE (todavía no existe ahí) y ayuda a empujar filtros lo antes posible.
Commas vs JOIN (no más coma-join)
SQL permite separar tablas por comas:
-- ❌ Evitar
SELECT c.nombre, p.total
FROM clientes c, pedidos p
WHERE p.cliente_id = c.id;
Funciona, pero es menos claro y propenso a olvidos. Preferí la sintaxis explícita:
-- ✅ Recomendado
SELECT c.nombre, p.total
FROM clientes c
JOIN pedidos p ON p.cliente_id = c.id;
FROM con vistas y tablas temporales
- Vistas:
FROM ventas_mensuales(encapsulan una consulta compleja). - Tablas temporales (según motor):
CREATE TEMP TABLE ...y luegoFROM temp_tabladurante la sesión. - Materialized views (si existen): permiten
FROMsobre vistas precalculadas para mejorar performance.
FROM y funciones/tablas especiales (según SGBD)
- PostgreSQL:
FROM generate_series(...),LATERAL(ver más abajo). - Oracle:
FROM DUALpara seleccionar constantes:SELECT 1 AS uno FROM DUAL; - SQL Server:
FROM (VALUES (...), (...)) AS v(col1,col2)para construir filas literales. - MySQL: a veces permite omitir
FROMal seleccionar constantes:SELECT 1;
LATERAL / APPLY: cuando la subconsulta depende de cada fila (avanzado)
En PostgreSQL, LATERAL permite que la subconsulta en FROM use columnas de la tabla previa:
SELECT c.id, x.top_pedido
FROM clientes c
CROSS JOIN LATERAL (
SELECT p.total AS top_pedido
FROM pedidos p
WHERE p.cliente_id = c.id
ORDER BY p.total DESC
LIMIT 1
) x;
En SQL Server, el patrón equivalente se hace con CROSS/OUTER APPLY.
Buenas prácticas con FROM
- Alias claros:
cpara clientes,ppara pedidos. Evitá nombres crípticos. - JOINs explícitos: hacen obvias las relaciones y evitan productos cartesianos accidentales.
- Filtrar temprano: empujá condiciones a
ON/WHEREpara reducir filas cuanto antes. - Seleccionar solo lo necesario: evitá
SELECT *en tablas grandes o tablas derivadas; mejora I/O y legibilidad. - Indexar claves de join: índices sobre
p.cliente_id,c.id, etc., aceleran el plan deFROM. - Prefiere CTE para claridad (o subconsulta en
FROM) cuando el cálculo intermedio mejora la lectura. - Nombrar columnas en tablas derivadas (algunos motores lo exigen).
- Verificar collation/tipos compatibles cuando unís texto entre tablas distintas.
Errores comunes (y cómo evitarlos)
- Olvidar la condición de JOIN
SELECT *
FROM clientes c
JOIN pedidos p; -- ❌ Falta ON → producto cartesiano gigantesco
Solución: agregá ON p.cliente_id = c.id o el USING(...) correspondiente.
- Ambigüedad de columnas
SELECT id -- ❌ ¿de qué tabla?
FROM clientes c JOIN pedidos p ON p.cliente_id = c.id;
Solución: calificá: c.id o p.id.
- Usar alias del SELECT dentro de WHERE
Recordá el orden lógico: el alias no existe aún. Usá la expresión original o envolvé en una subconsulta/CTE. - Tabla derivada sin alias
SELECT *
FROM (SELECT ...) -- ❌ algunos motores exigen alias
Solución: FROM (SELECT ...) AS t.
- FULL OUTER JOIN no soportado
MySQL no lo soporta directamente. Alternativas:LEFT JOIN ... UNION ... RIGHT JOIN(con cuidado). - JOIN condición en WHERE (con LEFT)
Poner filtros de la tabla derecha enWHEREpuede convertir unLEFT JOINenINNERsin querer:
-- ❌ Esto elimina filas sin match
FROM c LEFT JOIN p ON p.cliente_id = c.id
WHERE p.total > 0;
Solución: mover el filtro al ON o usar WHERE (p.total > 0 OR p.total IS NULL) según el objetivo.
Ejemplos prácticos más completos
Ventas por cliente con filtro de periodo
WITH ventas AS (
SELECT cliente_id, SUM(total) total
FROM pedidos
WHERE fecha BETWEEN '2025-01-01' AND '2025-06-30'
GROUP BY cliente_id
)
SELECT c.nombre, v.total
FROM clientes c
JOIN ventas v ON v.cliente_id = c.id
ORDER BY v.total DESC
LIMIT 10;
Productos sin ventas (anti-join)
SELECT pr.id, pr.nombre
FROM productos pr
LEFT JOIN pedidos_det pd ON pd.producto_id = pr.id
WHERE pd.producto_id IS NULL;
Top pedido por cliente (LATERAL / APPLY-style)
SELECT c.id, c.nombre, x.max_total
FROM clientes c
CROSS JOIN LATERAL (
SELECT MAX(p.total) AS max_total
FROM pedidos p
WHERE p.cliente_id = c.id
) x;
Preguntas frecuentes (FAQ)
1) ¿Puedo usar varias fuentes en FROM sin JOIN?
Se puede con comas, pero no es recomendable. Usá JOIN explícito para evitar errores y ser más claro.
2) ¿Qué diferencia hay entre subconsulta en FROM y CTE (WITH)?
Funcionalmente parecidas; la CTE suele ser más legible y reutilizable, y en algunos motores puede materializarse (o no). Elegí la que haga el código más claro y performante.
3) ¿Por qué mi LEFT JOIN “pierde” filas?
Probablemente pusiste filtros de la tabla derecha en WHERE en vez de ON. Eso transforma el LEFT en INNER. Ajustá la condición.
*4) ¿Es malo usar SELECT ?
No es “malo”, pero en producción conviene seleccionar solo lo necesario para reducir transferencia y acoplamiento.
5) ¿FROM siempre necesita una tabla real?
No. Podés usar vistas, CTE, tablas derivadas o incluso funciones/tablas especiales del motor (ej. DUAL en Oracle, VALUES en SQL Server).
Conclusión
FROM es la base de toda consulta SQL: define de dónde salen las filas que vas a transformar y presentar. Dominarlo implica saber elegir la fuente adecuada (tabla, vista, subconsulta, CTE), unir correctamente varias tablas con JOIN, y estructurar el plan de ejecución para filtrar lo antes posible y devolver solo lo necesario.
Con buenas prácticas (alias claros, joins explícitos, índices en claves de unión, filtros bien ubicados) tus consultas serán más legibles, correctas y rápidas.
Domina la selección de tablas en SELECT y FROM y vuelve al Glosario SQL completo.