LEFT, RIGHT y FULL JOIN: conservar filas sin coincidencia
Un INNER JOIN responde bien cuando solo nos interesan las filas relacionadas. El problema cambia cuando también necesitamos ver lo que quedó fuera: clientes sin pedidos, productos que nunca se han vendido o registros que aparecen en un origen, pero no en otro.
Las uniones externas conservan esas filas aunque no encuentren pareja. Las columnas procedentes del lado sin coincidencia se completan con NULL, que representa un valor desconocido o no disponible.
La idea
La dirección de la unión indica qué conjunto se conservará:
LEFT JOINconserva todas las filas de la tabla escrita a la izquierda.RIGHT JOINconserva todas las filas de la tabla escrita a la derecha.FULL JOINconserva las filas de ambos lados, hayan coincidido o no.
LEFT JOIN: conservar el lado izquierdo
LEFT JOIN conserva todas las filas del conjunto escrito antes de la unión y agrega las columnas de las coincidencias encontradas a la derecha.
FROM dbo.TablaIzquierda AS i
LEFT JOIN dbo.TablaDerecha AS d
ON d.IdRelacionado = i.Id
Queremos revisar todos los clientes, incluso aquellos que no han realizado pedidos:
SELECT
c.IdCliente,
c.Nombre,
p.IdPedido,
p.Estado
FROM dbo.Cliente AS c
LEFT JOIN dbo.Pedido AS p
ON p.IdCliente = c.IdCliente
ORDER BY
c.IdCliente,
p.IdPedido;
| IdCliente | Nombre | IdPedido | Estado |
|---|---|---|---|
| 1 | Ana Torres | 1 | Confirmado |
| 1 | Ana Torres | 2 | Enviado |
| 2 | Bruno Díaz | 3 | Pendiente |
| 3 | Carla Ruiz | NULL |
NULL |
| 4 | Diego Soto | 4 | Cancelado |
| 5 | Elena Mora | 5 | Confirmado |
Los identificadores 1, 2, 4 y 5 encuentran pedidos. El identificador 3, correspondiente a Carla, también permanece porque LEFT JOIN conserva todo el conjunto Cliente.
Carla permanece porque Cliente está a la izquierda. Como no existe un pedido relacionado con su identificador, las columnas provenientes de Pedido contienen NULL.
Este comportamiento permite encontrar específicamente clientes sin pedidos:
SELECT
c.IdCliente,
c.Nombre
FROM dbo.Cliente AS c
LEFT JOIN dbo.Pedido AS p
ON p.IdCliente = c.IdCliente
WHERE p.IdPedido IS NULL;
RIGHT JOIN
RIGHT JOIN conserva la tabla escrita a la derecha. Con Cliente antes de RIGHT JOIN y Pedido después, se conservan todas las filas de Pedido:
FROM dbo.TablaIzquierda AS i
RIGHT JOIN dbo.TablaDerecha AS d
ON d.IdRelacionado = i.Id
SELECT
c.IdCliente,
c.Nombre,
p.IdPedido,
p.Estado
FROM dbo.Cliente AS c
RIGHT JOIN dbo.Pedido AS p
ON p.IdCliente = c.IdCliente
ORDER BY
p.IdPedido;
RIGHT JOIN conserva todas las filas de Pedido. Cuando una fila no encuentra cliente, las columnas procedentes de Cliente se completan con NULL.
Carla queda fuera porque no tiene pedido. Con los datos del ejemplo, todos los pedidos encuentran cliente; por eso la región exclusiva de Pedido no contiene identificadores.
Para identificar solamente los pedidos que no encuentran cliente, filtramos las filas sin coincidencia:
SELECT
p.IdPedido,
p.IdCliente,
p.Estado
FROM dbo.Cliente AS c
RIGHT JOIN dbo.Pedido AS p
ON p.IdCliente = c.IdCliente
WHERE c.IdCliente IS NULL
ORDER BY p.IdPedido;
Esta segunda consulta representa solo la región exclusiva de Pedido. En los datos actuales no devuelve filas; si existiera un pedido sin cliente relacionado, aparecería aquí con sus datos de Pedido.
La misma operación puede escribirse con LEFT JOIN si se intercambia el orden de las tablas. El conjunto conservado depende de la tabla que queda en el lado indicado por la unión.
FULL JOIN
FULL JOIN resulta útil al comparar dos conjuntos. Conserva coincidencias, filas exclusivas de la izquierda y filas exclusivas de la derecha:
FROM dbo.TablaIzquierda AS i
FULL JOIN dbo.TablaDerecha AS d
ON d.IdRelacionado = i.Id
SELECT
a.Clave,
a.Valor AS ValorAnterior,
n.Valor AS ValorNuevo
FROM dbo.DatosAnteriores AS a
FULL JOIN dbo.DatosNuevos AS n
ON n.Clave = a.Clave;
Aquí se conservan las tres regiones: filas que solo existen en DatosAnteriores, coincidencias y filas que solo existen en DatosNuevos.
Qué conviene comprobar
La tabla opcional es el lado de una unión externa que puede no encontrar coincidencia y aportar columnas con NULL. Una condición sobre ese lado puede convertir accidentalmente un LEFT JOIN en el equivalente práctico de un INNER JOIN:
-- Los clientes sin pedido quedan fuera porque p.Estado es NULL.
WHERE p.Estado = 'Confirmado';
Cuando queremos conservarlos, esa condición puede formar parte de ON o debe contemplar explícitamente el valor nulo. La elección depende de qué filas pretendemos conservar, no de una regla mecánica.


