Un JOIN en SQL une filas de dos tablas que cumplen una condición, normalmente que un identificador coincida. INNER JOIN devuelve solo las filas que encuentran pareja en las dos tablas. LEFT JOIN devuelve todas las filas de la tabla de la izquierda y rellena con NULL lo que falta de la derecha. Ahí se decide qué filas desaparecen sin avisar.
En este tutorial lo vas a ver con dos tablas pequeñas de ventas: una de clientes y otra de pedidos. Los datos son inventados para el ejemplo, pero las consultas no son de pizarra: hemos ejecutado cada una el 11 de noviembre de 2026 en SQLite 3.43.2 y pegamos debajo la salida real. La sintaxis es la estándar que documenta PostgreSQL, así que te sirve en PostgreSQL y, con pocos cambios, en otros motores relacionales. Los matices que hemos encontrado los señalamos donde aparecen.
Las dos tablas de ventas del ejemplo
Para entender un JOIN necesitas dos tablas relacionadas y algún dato que no encaje. Usamos clientes (quién compra) y pedidos (qué compra y cuándo). Los pedidos apuntan al cliente con la columna cliente_id. Hay dos trampas a propósito: un cliente sin pedidos y un pedido cuyo cliente no existe.
Este es el script completo. Puedes pegarlo tal cual en SQLite, en PostgreSQL o en cualquier editor de SQL online y repetir todas las consultas del artículo:
Fíjate en dos filas antes de seguir. David (cliente 4) no tiene ningún pedido. Y el pedido 106 apunta al cliente 9, que no está en la tabla de clientes. En una base bien diseñada, una clave foránea (FOREIGN KEY) impediría ese pedido huérfano, pero en la vida real aparecen más de lo que parece: cargas desde Excel, clientes borrados, integraciones a medias. Son justo los casos en los que INNER y LEFT dan resultados distintos.
Qué hace un JOIN en SQL
Un JOIN combina filas de una tabla con filas de otra según una expresión que dice cuáles se emparejan. La documentación de PostgreSQL lo explica así en su tutorial Joins Between Tables: las consultas que acceden a varias tablas a la vez se llaman join queries y combinan filas indicando qué pares deben unirse.
La estructura de una consulta SQL con JOIN siempre tiene las mismas piezas:
Tres detalles que conviene fijar desde el principio:
La condición va en
ON. Es la regla de emparejamiento. Si ambas columnas se llaman igual, puedes usarUSING (columna)como atajo; la referencia de PostgreSQL lo describe como una forma abreviada de la igualdad.INNERyOUTERson opcionales. Según esa misma referencia,JOINa secas es un INNER JOIN, yLEFT JOINequivale aLEFT OUTER JOIN.Pon alias y cualifica las columnas. Escribir
c.nombreen vez denombreevita ambigüedades. PostgreSQL lo considera buen estilo porque la consulta no se rompe si mañana alguien añade una columna con el mismo nombre a la otra tabla.
Quizá veas también la sintaxis antigua, con las tablas separadas por comas y la condición en el WHERE (FROM pedidos, clientes WHERE ...). Da el mismo resultado que un INNER JOIN, pero la forma explícita con JOIN ... ON, introducida en SQL-92, separa la regla de unión de los filtros y se lee mucho mejor.
INNER JOIN: solo las filas que encuentran pareja
INNER JOIN devuelve únicamente las combinaciones en las que la condición se cumple en las dos tablas. Si un pedido no tiene cliente o un cliente no tiene pedidos, esa fila no aparece. Es el JOIN por defecto y el que usarás cuando solo te interesan los datos completos, por ejemplo, para listar ventas con el nombre de quien compró.
Salida real:
Cinco filas de seis pedidos. El 106 ha desaparecido porque el cliente 9 no existe, y David tampoco está porque no tiene pedidos. Nadie te avisa de ninguna de las dos cosas: la consulta funciona y el resultado parece correcto. Si sumaras los importes de esta salida, te saldrían 605 euros, cuando en la tabla de pedidos hay 680. Esos 75 euros de diferencia son el pedido huérfano.
LEFT JOIN: todas las filas de la tabla izquierda
LEFT JOIN conserva todas las filas de la tabla que escribes antes del JOIN, encuentren pareja o no. Si no la encuentran, las columnas de la tabla derecha salen como NULL. La documentación de PostgreSQL lo resume así: primero se hace el INNER JOIN y luego se añade cada fila de la izquierda que no casó, con nulos en la derecha.
Ahora partimos de clientes, porque la pregunta es «qué ha comprado cada cliente», incluidos los que no han comprado nada:
Salida real:
David aparece con NULL en pedido e importe. Ese NULL no significa «cero»: significa «no hay fila con la que emparejar». Y fíjate en otra cosa: Ana y Carmen salen dos veces. Un JOIN repite cada fila de la izquierda tantas veces como parejas encuentra a la derecha. Tenlo presente cuando cuentes o sumes, porque volveremos a ello.
El orden importa. clientes LEFT JOIN pedidos no es lo mismo que pedidos LEFT JOIN clientes: la tabla que manda, la que conserva todas sus filas, es siempre la que queda a la izquierda de la palabra JOIN.
Cuándo usar INNER JOIN y cuándo LEFT JOIN
Elegir entre INNER JOIN y LEFT JOIN depende de la pregunta, no de la tabla. Si habla de cosas que existen en las dos tablas («ventas con su cliente»), usa INNER. Si habla de todos los elementos de un lado, tengan o no datos en el otro («todos los clientes y lo que han comprado»), usa LEFT con ese lado a la izquierda.
Si tu pregunta es… | Usa | Tabla a la izquierda |
|---|---|---|
Ventas con el nombre del cliente | INNER JOIN | pedidos (o cualquiera) |
Todos los clientes y su facturación, aunque sea cero | LEFT JOIN | clientes |
Clientes que nunca han comprado | LEFT JOIN + | clientes |
Pedidos con un cliente que no existe | LEFT JOIN + | pedidos |
Todo, case o no case, para auditar datos | FULL OUTER JOIN | da igual |
Nuestro criterio para quien está empezando: ante la duda, escribe primero el LEFT JOIN desde la tabla principal del informe y mira si salen NULL. Si salen, ya sabes que hay filas sin pareja y decides con conocimiento si quieres verlas o descartarlas. Con un INNER JOIN directo, esas filas se pierden sin que te enteres, y en un informe de ventas eso suele ser dinero que no cuadra.
De tu primer JOIN a SQL avanzado
El máster en AI Data Analytics incluye el «Módulo 8 · SQL», que empieza desde cero: «el módulo SQL basics comienza desde lo básico y llega a SQL avanzado». Descarga el dossier y descubre todas las herramientas que vas a aprender a utilizar.
Tres consultas de negocio con LEFT JOIN
Con LEFT JOIN se resuelven tres preguntas que cualquier analista de datos recibe pronto: cuánto ha comprado cada cliente, quién no ha comprado nunca y qué ventas no tienen un cliente válido. Las tres usan las mismas tablas, y cada una tiene un detalle que, si se pasa por alto, da un resultado plausible pero equivocado.
Facturación por cliente (y la trampa de COUNT(*))
Para agrupar, el LEFT JOIN se combina con GROUP BY. Hemos puesto a propósito dos formas de contar para que veas la diferencia:
Salida real:
Mira la fila de David. COUNT(*) cuenta filas del resultado, y el LEFT JOIN le ha dado una fila llena de NULL, así que dice 1. COUNT(p.id) cuenta valores no nulos y dice 0, que es la respuesta correcta. Por la misma razón, SUM(p.importe) de David sería NULL; COALESCE(..., 0) lo convierte en 0 para que el informe no muestre un hueco.
Clientes que nunca han comprado
Es la consulta de «clientes dormidos» para una campaña. Se hace con un LEFT JOIN y quedándote solo con las filas que no encontraron pareja:
Salida real:
Comprueba siempre con IS NULL una columna que nunca pueda ser nula en una fila real, como la clave primaria p.id. Si comprobaras una columna opcional, colarías clientes que sí tienen pedidos con ese campo vacío.
Ventas sin cliente válido
La misma idea, con las tablas al revés, sirve para auditar datos: encontrar pedidos que apuntan a un cliente inexistente.
Salida real:
Ahí están los 75 euros que el INNER JOIN se comía. Esta consulta merece un sitio fijo en cualquier revisión de calidad de datos antes de montar un dashboard.
El error que no da error: filtrar en WHERE lo que iba en ON
Con un LEFT JOIN, poner un filtro en el ON o en el WHERE cambia el resultado. En ON se aplica al emparejar, así que la fila de la izquierda se conserva aunque no case. En WHERE se aplica después y elimina las filas con NULL, con lo que tu LEFT JOIN se convierte, en la práctica, en un INNER JOIN.
Pongamos que te piden la facturación de octubre de todos los clientes, con cero para quien no compró ese mes. Primero, con el filtro de fecha dentro del ON:
Salida real:
Ahora, la misma consulta con el filtro en el WHERE:
Salida real:
Carmen y David han desaparecido. Carmen compró en septiembre, no en octubre; David no ha comprado nunca. En las dos filas, p.fecha es NULL tras el LEFT JOIN, y NULL >= '2026-10-01' no es verdadero, así que el WHERE las descarta. La documentación de PostgreSQL lo explica con un ejemplo parecido: una restricción en ON se procesa antes del JOIN y una en WHERE después, algo que no importa en un INNER JOIN pero sí, y mucho, en un JOIN externo.
Es el error que más cuesta detectar, porque la consulta funciona y los números que salen son ciertos. Simplemente faltan filas. La regla práctica: en un LEFT JOIN, las condiciones sobre la tabla de la derecha van en el ON; en el WHERE, solo las que filtran la tabla de la izquierda.
RIGHT JOIN y FULL OUTER JOIN
RIGHT JOIN es el espejo de LEFT JOIN: conserva todas las filas de la tabla de la derecha. FULL OUTER JOIN conserva las de las dos tablas y rellena con NULL los huecos de cada lado. PostgreSQL admite ambos. En SQLite llegaron tarde: según sus notas de la versión 3.39.0, de junio de 2022, hasta entonces no se admitían.
Con RIGHT JOIN, partiendo de clientes, recuperas todos los pedidos, también el huérfano:
Salida real:
Es exactamente lo mismo que escribir pedidos LEFT JOIN clientes. Por eso, en la práctica, casi todo el mundo reescribe los RIGHT JOIN como LEFT JOIN: leer siempre de izquierda a derecha, con la tabla principal delante, hace las consultas largas mucho más fáciles de revisar.
FULL OUTER JOIN muestra todo a la vez, lo que lo hace muy útil para auditar dos tablas que deberían cuadrar:
Salida real:
Salen las siete filas: los cinco pedidos que casan, el pedido sin cliente y el cliente sin pedidos. Un matiz si vas a repetirlo en PostgreSQL: la fila con NULL en c.id aparecerá al final, no al principio. SQLite trata NULL como el valor más pequeño al ordenar, mientras que PostgreSQL lo ordena como el más grande cuando el orden es ascendente. Si necesitas el mismo orden en los dos, añade NULLS FIRST o NULLS LAST al ORDER BY. Antes de usar FULL OUTER JOIN en otro motor, comprueba en su documentación que lo admite.
Errores frecuentes al escribir un JOIN
Los fallos habituales con JOIN casi nunca producen un mensaje de error. Producen un resultado con filas de más o de menos que parece correcto, y por eso conviene repasar esta lista antes de dar por buena cualquier consulta SQL que alimente un informe o un dashboard:
Olvidar la condición de unión. Si separas las tablas con comas y te olvidas del
WHERE, o escribes unJOINsinONen un motor que lo tolera (SQLite lo hace), cada fila de una tabla se combina con todas las de la otra: un producto cartesiano. Con nuestras tablas salen 24 filas, 4 clientes por 6 pedidos; lo hemos comprobado tanto conJOINsinONcomo conCROSS JOIN. Con tablas reales de miles de filas, la consulta puede no terminar nunca.Columnas sin cualificar. Si las dos tablas tienen una columna
id, escribirida secas da error o, peor, toma la que no querías. Usa siempre alias.Contar con
COUNT(*)tras un LEFT JOIN. Cuenta las filas con NULL como si fueran datos. Cuenta una columna de la tabla derecha, comoCOUNT(p.id).Filtrar la tabla derecha en el
WHERE. Convierte el LEFT JOIN en un INNER JOIN, como has visto con octubre.Sumar después de duplicar filas. Si unes clientes con pedidos y luego sumas una columna del cliente (por ejemplo, un límite de crédito), cada cliente se suma tantas veces como pedidos tiene. Agrega antes en una subconsulta o suma solo columnas de la tabla «de muchos».
Confiar en que no hay huérfanos. Ejecuta la consulta de ventas sin cliente válido de vez en cuando. Si la base no tiene claves foráneas, aparecerán.
Si trabajas también con Python, la misma lógica existe en pandas: la función merge une tablas «al estilo de una base de datos», con sus variantes inner, left, right y outer. Lo explicamos con datos reales en primeros pasos con pandas en Python. Y si te preguntas por qué este tipo de consultas es propio de las bases relacionales y no de otras, lo tienes en SQL vs NoSQL.
Siguiente paso: aprender SQL de lo básico a lo avanzado
Dominar INNER y LEFT JOIN cubre buena parte de las consultas SQL que escribe un analista en su día a día. Lo siguiente es encadenar varias tablas, usar subconsultas y CTE, y funciones de ventana para comparar periodos. Ese salto es más llevadero con datos reales y alguien que revise tus consultas.
Si quieres hacerlo de forma guiada, el máster en AI Data Analytics de ThePower Tech School incluye en su plan de estudios el «Módulo 8 · SQL». Según su ficha, consultada el 11-11-2026, a la pregunta de si hace falta saber SQL avanzado antes responde: «Desde cero. Si haces especialización AE, el módulo SQL basics comienza desde lo básico y llega a SQL avanzado». La especialización Analytics Engineer, la de enfoque técnico, suma a SQL herramientas como Python, Databricks, PySpark y dbt.
Si estás valorando la profesión, te puede servir saber qué hace un data analyst y qué skills necesita y cuál es el sueldo de un analista de datos en España según las fuentes públicas.
Preguntas frecuentes sobre JOIN en SQL
¿Qué diferencia hay entre INNER JOIN y LEFT JOIN?
INNER JOIN devuelve solo las filas que encuentran pareja en las dos tablas según la condición del ON. LEFT JOIN devuelve todas las filas de la tabla de la izquierda y, cuando no encuentran pareja, rellena con NULL las columnas de la derecha. Con INNER pierdes las filas sin coincidencia; con LEFT las ves y decides qué hacer con ellas.
¿JOIN y INNER JOIN son lo mismo?
Sí. La palabra INNER es opcional y JOIN a secas equivale a INNER JOIN, tal como recoge la documentación de PostgreSQL. Lo mismo pasa con OUTER: LEFT JOIN y LEFT OUTER JOIN son idénticos. Escribir INNER explícitamente no cambia el resultado, pero ayuda a que quien lea la consulta vea de un vistazo qué tipo de unión has elegido.
¿Por qué mi JOIN devuelve filas duplicadas?
Porque cada fila se repite tantas veces como coincidencias tiene en la otra tabla. Si un cliente tiene tres pedidos, aparecerá tres veces. No es un fallo, es cómo funciona el JOIN, pero afecta a los conteos y las sumas. También puede deberse a una condición incompleta en el ON o a valores repetidos en la columna que creías única.
¿Qué es un FULL OUTER JOIN y cuándo se usa?
Es una unión que conserva todas las filas de las dos tablas: las que casan, las de la izquierda sin pareja y las de la derecha sin pareja, con NULL en los huecos. Se usa sobre todo para auditar o conciliar datos, por ejemplo, comprobar que dos sistemas tienen los mismos clientes. PostgreSQL lo admite, y SQLite desde la versión 3.39.0.
¿Cómo empiezo a aprender SQL desde cero?
Empieza por SELECT, WHERE y ORDER BY sobre una sola tabla, y pasa a JOIN en cuanto las domines, porque es lo que de verdad abre el análisis. Practica con tablas pequeñas cuyo resultado puedas comprobar a mano, como las de este artículo. SQLite no necesita instalar un servidor, así que es una buena forma de empezar hoy mismo.








