Diferencia entre INNER JOIN, LEFT JOIN, RIGHT JOIN y FULL JOIN

D
DanisCh
• 12 min de lectura
Diferencia entre INNER JOIN, LEFT JOIN, RIGHT JOIN y FULL JOIN
SQL Conceptos de Base de Datos

Los JOINs son la herramienta fundamental para combinar datos de varias tablas en SQL. Son el corazón de las bases de datos relacionales: la razón por la que los datos se organizan en tablas separadas en lugar de una sola tabla gigante es precisamente para poder combinarlos de formas flexibles. Sin embargo, la diferencia entre los distintos tipos de JOIN confunde a muchos desarrolladores que empiezan con SQL.

En esta guía vas a entender cada tipo de JOIN con ejemplos reales y visualizaciones de los datos que devuelve cada uno.

El esquema de ejemplo

Vamos a usar dos tablas pequeñas para que los resultados sean fáciles de seguir. Un conjunto de clientes y un conjunto de pedidos, con la particularidad de que no todos los clientes tienen pedidos y no todos los pedidos tienen un cliente registrado (para ilustrar bien las diferencias).

CREATE TABLE clientes (
  id     INT PRIMARY KEY,
  nombre VARCHAR(100)
);

CREATE TABLE pedidos (
  id         INT PRIMARY KEY,
  cliente_id INT,
  total      DECIMAL(10, 2),
  producto   VARCHAR(100)
);

INSERT INTO clientes VALUES
  (1, 'Ana García'),
  (2, 'Carlos López'),
  (3, 'María Torres'),
  (4, 'Jorge Ramírez');   -- no tiene pedidos

INSERT INTO pedidos VALUES
  (101, 1,    59.99, 'Teclado'),
  (102, 1,   129.00, 'Monitor'),
  (103, 2,    19.99, 'Ratón'),
  (104, NULL, 89.00, 'Auriculares'),  -- sin cliente registrado
  (105, 5,    45.00, 'Webcam');       -- cliente_id 5 no existe en clientes

Estado de las tablas:

-- clientes:
-- id  nombre
-- 1   Ana García
-- 2   Carlos López
-- 3   María Torres
-- 4   Jorge Ramírez   ← no tiene ningún pedido

-- pedidos:
-- id   cliente_id  total   producto
-- 101  1           59.99   Teclado
-- 102  1           129.00  Monitor
-- 103  2           19.99   Ratón
-- 104  NULL        89.00   Auriculares  ← sin cliente
-- 105  5           45.00   Webcam       ← cliente_id 5 no existe

INNER JOIN: solo las filas que coinciden en ambas tablas

El INNER JOIN devuelve únicamente las filas que tienen correspondencia en ambas tablas. Si una fila de la tabla izquierda no tiene coincidencia en la tabla derecha (o viceversa), esa fila queda fuera del resultado.

SELECT
  c.id     AS cliente_id,
  c.nombre AS cliente,
  p.id     AS pedido_id,
  p.total,
  p.producto
FROM clientes c
INNER JOIN pedidos p ON p.cliente_id = c.id;
-- Resultado:
-- cliente_id  cliente       pedido_id  total   producto
-- 1           Ana García    101        59.99   Teclado
-- 1           Ana García    102        129.00  Monitor
-- 2           Carlos López  103        19.99   Ratón

-- ¿Qué quedó fuera?
-- Jorge Ramírez (id=4): no tiene ningún pedido → excluido
-- María Torres (id=3):  no tiene ningún pedido → excluida
-- Pedido 104 (cliente_id=NULL):  NULL no coincide con ningún id → excluido
-- Pedido 105 (cliente_id=5):     no existe cliente con id=5 → excluido

La palabra INNER es opcional: JOIN a secas es un INNER JOIN. Son equivalentes.

-- Estas dos consultas son idénticas
SELECT * FROM clientes c JOIN pedidos p ON p.cliente_id = c.id;
SELECT * FROM clientes c INNER JOIN pedidos p ON p.cliente_id = c.id;

Úsalo cuando quieras solo los registros que tienen relación en las dos tablas. Es el más común en el día a día.

LEFT JOIN: todas las filas de la izquierda, las coincidentes de la derecha

El LEFT JOIN (o LEFT OUTER JOIN) devuelve todas las filas de la tabla izquierda (la que aparece antes del JOIN), más las filas coincidentes de la tabla derecha. Si una fila de la tabla izquierda no tiene correspondencia en la derecha, aparece en el resultado con NULL en las columnas de la tabla derecha.

SELECT
  c.id     AS cliente_id,
  c.nombre AS cliente,
  p.id     AS pedido_id,
  p.total,
  p.producto
FROM clientes c
LEFT JOIN pedidos p ON p.cliente_id = c.id;
-- Resultado:
-- cliente_id  cliente        pedido_id  total   producto
-- 1           Ana García     101        59.99   Teclado
-- 1           Ana García     102        129.00  Monitor
-- 2           Carlos López   103        19.99   Ratón
-- 3           María Torres   NULL       NULL    NULL     ← sin pedidos, pero aparece
-- 4           Jorge Ramírez  NULL       NULL    NULL     ← sin pedidos, pero aparece

-- ¿Qué quedó fuera?
-- Pedido 104 (cliente_id=NULL): no coincide con ningún cliente → excluido
-- Pedido 105 (cliente_id=5):    no existe cliente con id=5 → excluido

Caso de uso clásico del LEFT JOIN: encontrar filas sin correspondencia

Una de las aplicaciones más útiles del LEFT JOIN es encontrar exactamente los registros que no tienen correspondencia: clientes sin pedidos, productos sin ventas, empleados sin manager asignado.

-- Clientes que NO han hecho ningún pedido
SELECT c.id, c.nombre
FROM clientes c
LEFT JOIN pedidos p ON p.cliente_id = c.id
WHERE p.id IS NULL;   -- ← la clave: filtrar donde el JOIN no encontró nada

-- Resultado:
-- id  nombre
-- 3   María Torres
-- 4   Jorge Ramírez

Este patrón (LEFT JOIN + WHERE tabla_derecha.columna IS NULL) es equivalente a NOT EXISTS o NOT IN, pero suele ser más eficiente y más claro de leer.

RIGHT JOIN: todas las filas de la derecha, las coincidentes de la izquierda

El RIGHT JOIN (o RIGHT OUTER JOIN) es el espejo del LEFT JOIN: devuelve todas las filas de la tabla derecha (la que aparece después del JOIN), más las filas coincidentes de la tabla izquierda. Las filas de la tabla derecha sin correspondencia aparecen con NULL en las columnas de la tabla izquierda.

SELECT
  c.id     AS cliente_id,
  c.nombre AS cliente,
  p.id     AS pedido_id,
  p.total,
  p.producto
FROM clientes c
RIGHT JOIN pedidos p ON p.cliente_id = c.id;
-- Resultado:
-- cliente_id  cliente       pedido_id  total   producto
-- 1           Ana García    101        59.99   Teclado
-- 1           Ana García    102        129.00  Monitor
-- 2           Carlos López  103        19.99   Ratón
-- NULL        NULL          104        89.00   Auriculares  ← sin cliente, pero aparece
-- NULL        NULL          105        45.00   Webcam       ← sin cliente válido, aparece

-- ¿Qué quedó fuera?
-- María Torres (id=3):   no tiene pedidos → excluida (estaba en la tabla izquierda)
-- Jorge Ramírez (id=4):  no tiene pedidos → excluido (estaba en la tabla izquierda)

RIGHT JOIN es poco habitual: por qué

En la práctica, el RIGHT JOIN se usa muy poco porque puedes conseguir exactamente el mismo resultado invirtiendo el orden de las tablas y usando LEFT JOIN. La mayoría de desarrolladores prefieren escribir siempre LEFT JOIN y cambiar el orden de las tablas en lugar de mezclar LEFT y RIGHT en la misma consulta.

-- Estas dos consultas devuelven el mismo resultado:
SELECT c.nombre, p.producto
FROM clientes c
RIGHT JOIN pedidos p ON p.cliente_id = c.id;

-- Equivalente más legible con LEFT JOIN:
SELECT c.nombre, p.producto
FROM pedidos p
LEFT JOIN clientes c ON c.id = p.cliente_id;

FULL JOIN: todas las filas de ambas tablas

El FULL JOIN (o FULL OUTER JOIN) devuelve todas las filas de ambas tablas, haya coincidencia o no. Donde no hay coincidencia, las columnas de la tabla que no aportó datos aparecen como NULL. Es la unión completa de LEFT JOIN y RIGHT JOIN.

-- PostgreSQL: FULL JOIN es soporte nativo
SELECT
  c.id     AS cliente_id,
  c.nombre AS cliente,
  p.id     AS pedido_id,
  p.total,
  p.producto
FROM clientes c
FULL JOIN pedidos p ON p.cliente_id = c.id;
-- Resultado:
-- cliente_id  cliente        pedido_id  total   producto
-- 1           Ana García     101        59.99   Teclado
-- 1           Ana García     102        129.00  Monitor
-- 2           Carlos López   103        19.99   Ratón
-- 3           María Torres   NULL       NULL    NULL     ← cliente sin pedidos
-- 4           Jorge Ramírez  NULL       NULL    NULL     ← cliente sin pedidos
-- NULL        NULL           104        89.00   Auriculares  ← pedido sin cliente
-- NULL        NULL           105        45.00   Webcam       ← pedido sin cliente

FULL JOIN en MySQL: no existe nativamente

MySQL no soporta FULL JOIN. La forma de simularlo es combinar un LEFT JOIN y un RIGHT JOIN con UNION:

-- MySQL: simular FULL JOIN con UNION de LEFT y RIGHT JOIN
SELECT c.id AS cliente_id, c.nombre, p.id AS pedido_id, p.total, p.producto
FROM clientes c
LEFT JOIN pedidos p ON p.cliente_id = c.id

UNION

SELECT c.id AS cliente_id, c.nombre, p.id AS pedido_id, p.total, p.producto
FROM clientes c
RIGHT JOIN pedidos p ON p.cliente_id = c.id;

-- UNION elimina duplicados automáticamente
-- Si quieres conservar duplicados, usa UNION ALL (más eficiente pero puede repetir filas)

CROSS JOIN: el producto cartesiano

El CROSS JOIN combina cada fila de la tabla izquierda con cada fila de la tabla derecha. Si la tabla A tiene 4 filas y la tabla B tiene 5 filas, el resultado tendrá 4 × 5 = 20 filas. No lleva condición ON.

SELECT c.nombre, p.producto
FROM clientes c
CROSS JOIN pedidos p;

-- Resultado: 4 clientes × 5 pedidos = 20 filas
-- (cada cliente combinado con cada pedido, sin tener en cuenta relaciones)

-- Caso de uso real: generar todas las combinaciones posibles
CREATE TABLE tallas    (talla   VARCHAR(5));
CREATE TABLE colores   (color   VARCHAR(20));
CREATE TABLE materiales (material VARCHAR(20));

INSERT INTO tallas    VALUES ('S'), ('M'), ('L'), ('XL');
INSERT INTO colores   VALUES ('Negro'), ('Blanco'), ('Rojo');
INSERT INTO materiales VALUES ('Algodón'), ('Poliéster');

-- Generar el catálogo completo: 4 × 3 × 2 = 24 combinaciones
SELECT t.talla, c.color, m.material
FROM tallas t
CROSS JOIN colores c
CROSS JOIN materiales m
ORDER BY t.talla, c.color, m.material;

SELF JOIN: unir una tabla consigo misma

Un SELF JOIN es cuando una tabla se une consigo misma. Se usa para relaciones jerárquicas o cuando una tabla tiene una columna que referencia otra fila de la misma tabla.

CREATE TABLE empleados (
  id         INT PRIMARY KEY,
  nombre     VARCHAR(100),
  manager_id INT   -- referencia al id de otro empleado de la misma tabla
);

INSERT INTO empleados VALUES
  (1, 'Ana (CEO)',           NULL),
  (2, 'Carlos (Ingeniería)', 1),
  (3, 'María (Marketing)',   1),
  (4, 'Jorge (Dev)',         2),
  (5, 'Lucía (Dev)',         2);

-- Mostrar cada empleado con el nombre de su manager
SELECT
  e.nombre    AS empleado,
  m.nombre    AS manager
FROM empleados e
LEFT JOIN empleados m ON m.id = e.manager_id;   -- la misma tabla con alias distintos

-- Resultado:
-- empleado              manager
-- Ana (CEO)             NULL
-- Carlos (Ingeniería)   Ana (CEO)
-- María (Marketing)     Ana (CEO)
-- Jorge (Dev)           Carlos (Ingeniería)
-- Lucía (Dev)           Carlos (Ingeniería)

Comparativa visual de todos los JOINs

Tipo de JOINFilas de la izquierda sin coincidenciaFilas coincidentesFilas de la derecha sin coincidencia
INNER JOINNoSíNo
LEFT JOINSí (con NULL en cols de la derecha)SíNo
RIGHT JOINNoSíSí (con NULL en cols de la izquierda)
FULL JOINSí (con NULL)SíSí (con NULL)
CROSS JOINTodas las combinaciones posibles, sin condición

Múltiples JOINs en una sola consulta

-- Una consulta real con varios JOINs encadenados
CREATE TABLE categorias (
  id     INT PRIMARY KEY,
  nombre VARCHAR(50)
);

CREATE TABLE productos (
  id           INT PRIMARY KEY,
  nombre       VARCHAR(100),
  categoria_id INT REFERENCES categorias(id)
);

-- Consulta que une clientes, pedidos, productos y categorías
SELECT
  c.nombre      AS cliente,
  p.id          AS pedido_id,
  pr.nombre     AS producto,
  cat.nombre    AS categoria,
  p.total
FROM pedidos p
INNER JOIN clientes   c   ON c.id   = p.cliente_id
INNER JOIN productos  pr  ON pr.id  = p.producto_id
LEFT  JOIN categorias cat ON cat.id = pr.categoria_id   -- LEFT porque puede no tener categoría
WHERE p.total > 50
ORDER BY p.total DESC;

El orden en que escribes los JOINs no afecta al resultado (el optimizador reordena según lo que sea más eficiente), pero sí afecta a la legibilidad. La convención habitual es ir de la tabla central hacia afuera: empieza por la tabla que más filtra o que es el centro lógico de la consulta y añade los JOINs hacia las tablas relacionadas.

Errores comunes con los JOINs

Olvidar la condición ON y generar un producto cartesiano accidental

-- ❌ Sin ON: CROSS JOIN accidental (puede devolver millones de filas)
SELECT * FROM clientes, pedidos;   -- sintaxis antigua, une todo con todo

-- ❌ También sin ON en sintaxis moderna (error o producto cartesiano según el motor)
SELECT * FROM clientes JOIN pedidos;

-- ✅ Siempre especifica la condición de unión
SELECT * FROM clientes c JOIN pedidos p ON p.cliente_id = c.id;

Confundir el lado del LEFT JOIN

-- Si quieres TODOS los pedidos aunque no tengan cliente,
-- el pedido debe estar en la tabla izquierda (antes del JOIN)

-- ❌ Esto devuelve todos los clientes (izquierda), no todos los pedidos
SELECT * FROM clientes c LEFT JOIN pedidos p ON p.cliente_id = c.id;

-- ✅ Esto devuelve todos los pedidos (izquierda), con o sin cliente
SELECT * FROM pedidos p LEFT JOIN clientes c ON c.id = p.cliente_id;

Multiplicar filas inesperadamente

-- Si una fila de la tabla izquierda coincide con VARIAS de la derecha,
-- aparecerá repetida en el resultado

-- Ana García tiene 2 pedidos → aparece 2 veces con INNER JOIN
SELECT c.nombre, p.producto
FROM clientes c
INNER JOIN pedidos p ON p.cliente_id = c.id;

-- cliente      producto
-- Ana García   Teclado
-- Ana García   Monitor   ← repetida, es correcto si quieres el detalle de pedidos

-- Si solo quieres UNA fila por cliente (el total), usa GROUP BY
SELECT c.nombre, COUNT(p.id) AS num_pedidos, SUM(p.total) AS total_gastado
FROM clientes c
LEFT JOIN pedidos p ON p.cliente_id = c.id
GROUP BY c.id, c.nombre;

Resumen: cuándo usar cada JOIN

  • Usa INNER JOIN cuando quieras solo los registros que tienen relación en ambas tablas. Es el más común: pedidos con sus clientes, productos con sus categorías.
  • Usa LEFT JOIN cuando quieras todos los registros de la tabla izquierda, haya o no coincidencia en la derecha. Muy útil para encontrar registros huérfanos (WHERE tabla_derecha.id IS NULL) o para informes que deben incluir todos los registros aunque algunos no tengan datos relacionados.
  • Usa RIGHT JOIN raramente: en casi todos los casos puedes invertir el orden de las tablas y usar LEFT JOIN, que es más fácil de leer y razonar.
  • Usa FULL JOIN cuando necesites todos los registros de ambas tablas sin importar si tienen correspondencia o no. Útil para auditorías y reconciliaciones de datos. En MySQL, simúlalo con UNION de LEFT y RIGHT JOIN.
  • Usa CROSS JOIN cuando necesites todas las combinaciones posibles entre dos conjuntos. Poco frecuente, pero indispensable para generar catálogos o matrices.
  • Usa SELF JOIN para relaciones jerárquicas dentro de la misma tabla: empleado-manager, categoría-subcategoría.

¿Te ha gustado esta entrada?

Compártela con tus compañeros para que también sigan aprendiendo.

Comunidad y Comentarios

0 COMENTARIOS

No hay comentarios todavía. Sé el primero en compartir tu opinión.

Escribe tu opinión
Respondiendo a