LEFT JOIN

El LEFT JOIN (también llamado LEFT OUTER JOIN) devuelve todas las filas de la tabla izquierda, tengan o no correspondencia en la tabla derecha. Cuando no hay coincidencia, las columnas de la tabla derecha se rellenan con NULL. Esto lo diferencia del INNER JOIN, que descarta las filas sin correspondencia.

El LEFT JOIN es esencial cuando necesitas preservar todos los registros de una tabla principal y enriquecerlos con información de otra tabla que puede no existir para todos ellos.

Sintaxis

SELECT columnas
FROM tabla_izquierda
LEFT JOIN tabla_derecha ON tabla_izquierda.columna = tabla_derecha.columna;

La tabla que aparece después de FROM es la tabla izquierda (la que se preserva completa). La tabla que aparece después de LEFT JOIN es la derecha (la que puede aportar NULLs cuando no hay coincidencia).

Cómo funciona

El proceso es similar al INNER JOIN, pero con una diferencia crucial en el paso final:

  1. MySQL toma cada fila de la tabla izquierda.
  2. Para cada una, busca en la tabla derecha las filas que cumplan la condición del ON.
  3. Si encuentra coincidencias, combina las columnas de ambas filas, igual que un INNER JOIN.
  4. Si no encuentra ninguna coincidencia, la fila de la tabla izquierda se mantiene en el resultado, y todas las columnas de la tabla derecha se llenan con NULL.

El resultado siempre contiene al menos tantas filas como la tabla izquierda. Si una fila de la tabla izquierda tiene varias coincidencias en la derecha, aparece repetida (una vez por cada coincidencia).

LEFT JOIN básico

Todos los clientes con el número de pedidos que han realizado, incluyendo aquellos que no han comprado nada:

SELECT
    cl.nombre,
    cl.apellidos,
    COUNT(p.id) AS total_pedidos
FROM clientes cl
LEFT JOIN pedidos p ON cl.id = p.cliente_id
GROUP BY cl.id, cl.nombre, cl.apellidos
ORDER BY total_pedidos, cl.nombre;
nombreapellidostotal_pedidos
DiegoCastro Iglesias0
AlejandroSerrano Blanco1
AndrésVega Romero1
CarmenRuiz Jiménez1
ElenaRomero Navarro1
FernandoMolina Ortega1
IsabelGil Molina1
JavierMoreno Díaz1
LauraLópez Sánchez1
LucíaDíaz Hernández1
MartaBlanco Castro1
MiguelHernández Torres1
PaulaOrtega Serrano1
RobertoNavarro Gil1
SaraTorres Vega1
AnaMartínez Ruiz2
CarlosRodríguez Martín2
DavidSánchez Moreno2
PedroFernández Castro2
MaríaGarcía López3

Diego Castro aparece con 0 pedidos. Con un INNER JOIN, esta fila simplemente no existiría en el resultado. El LEFT JOIN garantiza que los 20 clientes aparezcan, tengan o no pedidos.

Fíjate que usamos COUNT(p.id) y no COUNT(*). Es una distinción importante: COUNT(*) contaría la fila de Diego como 1 (porque la fila existe en el resultado), mientras que COUNT(p.id) devuelve 0 porque p.id es NULL cuando no hay pedido.

Encontrar registros sin correspondencia

Uno de los usos más potentes del LEFT JOIN es encontrar registros que no tienen relación en otra tabla. El patrón es hacer un LEFT JOIN y filtrar con WHERE ... IS NULL:

SELECT cl.nombre, cl.apellidos, cl.email
FROM clientes cl
LEFT JOIN pedidos p ON cl.id = p.cliente_id
WHERE p.id IS NULL;
nombreapellidosemail
DiegoCastro Iglesiasdiego.castro@email.com

Diego es el único cliente que nunca ha hecho un pedido. Este patrón es extremadamente útil: el LEFT JOIN incluye a todos los clientes, y el WHERE p.id IS NULL filtra para quedarse solo con aquellos donde no hubo coincidencia en la tabla de pedidos.

Productos sin reseñas

El mismo patrón para encontrar productos que nadie ha reseñado:

SELECT p.nombre, p.precio
FROM productos p
LEFT JOIN resenas r ON p.id = r.producto_id
WHERE r.id IS NULL
ORDER BY p.precio DESC;
nombreprecio
Lenovo ThinkPad X11549.00
Sofá 3 plazas599.00
Estantería modular149.99
Zapatillas trail149.99
Vaqueros slim fit59.99
Funda iPhone silicona49.99
Pantalón chino49.99
Sartén antiadherente 28cm39.99
Camiseta técnica running34.99
Eloquent JavaScript34.99
Diseño de APIs29.99
Cable USB-C a Lightning19.99
Banda elástica set x519.99
El nombre del viento16.99

14 de nuestros 30 productos no tienen ninguna reseña. El Lenovo ThinkPad, con un precio de 1549 euros, es el producto más caro sin valoraciones. Esta información sería útil para un equipo de marketing que quiera incentivar reseñas en productos de alto valor.

Productos que nunca se han vendido

Podemos buscar productos que no aparecen en ningún pedido:

SELECT p.nombre, p.precio, p.stock
FROM productos p
LEFT JOIN detalle_pedidos dp ON p.id = dp.producto_id
WHERE dp.id IS NULL
ORDER BY p.nombre;
nombrepreciostock
Cargador USB-C 65W35.99150
Estantería modular149.9922
Sofá 3 plazas599.008

Solo 3 productos no se han vendido nunca. El sofá de 3 plazas tiene apenas 8 unidades en stock y ninguna venta, lo que podría indicar un problema de visibilidad o de precio.

Categorías sin productos directos

Las 5 categorías principales de nuestra base de datos (Electrónica, Ropa, Hogar, Deportes, Libros) son categorías padre: no tienen productos asignados directamente, ya que los productos pertenecen a sus subcategorías:

SELECT c.nombre AS categoria, c.descripcion
FROM categorias c
LEFT JOIN productos p ON c.id = p.categoria_id
WHERE p.id IS NULL
ORDER BY c.nombre;
categoriadescripcion
DeportesEquipamiento deportivo y fitness
ElectrónicaDispositivos electrónicos y accesorios
HogarArtículos para el hogar y decoración
LibrosLibros físicos y digitales
RopaRopa y complementos para hombre y mujer

Estas categorías actúan como agrupadores de nivel superior. Sus subcategorías (Smartphones, Portátiles, Camisetas, etc.) son las que contienen los productos reales.

LEFT JOIN con agregación

Al combinar LEFT JOIN con funciones de agregación, recuerda usar COALESCE para convertir los NULL en valores legibles:

SELECT
    cl.nombre,
    cl.apellidos,
    COUNT(p.id) AS total_pedidos,
    COALESCE(SUM(p.total), 0) AS gasto_total
FROM clientes cl
LEFT JOIN pedidos p ON cl.id = p.cliente_id
GROUP BY cl.id, cl.nombre, cl.apellidos
ORDER BY gasto_total DESC
LIMIT 5;
nombreapellidostotal_pedidosgasto_total
PedroFernández Castro21979.97
MaríaGarcía López31849.95
SaraTorres Vega11549.00
CarmenRuiz Jiménez11399.00
CarlosRodríguez Martín21029.98

Sin COALESCE, el gasto total de Diego (el cliente sin pedidos) aparecería como NULL en lugar de 0. COALESCE(SUM(p.total), 0) sustituye el NULL por 0 para que la columna sea consistente.

Pedro lidera en gasto total con casi 1980 euros en solo 2 pedidos. María ha hecho más pedidos (3) pero ha gastado un poco menos.

LEFT JOIN con condición adicional en el ON

Puedes añadir condiciones al ON además de la relación entre claves. Esto es diferente de añadirlas al WHERE, y la diferencia importa mucho con LEFT JOIN:

SELECT
    cl.nombre,
    COUNT(p.id) AS pedidos_entregados
FROM clientes cl
LEFT JOIN pedidos p ON cl.id = p.cliente_id
    AND p.estado = 'entregado'
GROUP BY cl.id, cl.nombre
ORDER BY pedidos_entregados DESC, cl.nombre
LIMIT 8;
nombrepedidos_entregados
María2
Ana1
Carlos1
Isabel1
Laura1
Pedro1
Andrés0
Carmen0

La condición AND p.estado = 'entregado' está en el ON, no en el WHERE. Esto significa que solo se unen los pedidos entregados, pero los clientes sin pedidos entregados siguen apareciendo (con 0). Si hubiéramos puesto esa condición en el WHERE, los clientes sin pedidos entregados desaparecerían del resultado, porque WHERE filtra después del JOIN y elimina las filas con NULL.

Esta distinción entre poner condiciones en el ON o en el WHERE es una de las trampas más comunes al usar LEFT JOIN.

LEFT JOIN vs INNER JOIN

La diferencia en una sola frase: INNER JOIN descarta las filas sin correspondencia, LEFT JOIN las conserva con NULLs.

-- INNER JOIN: solo clientes CON pedidos (19 filas)
SELECT DISTINCT cl.nombre
FROM clientes cl
INNER JOIN pedidos p ON cl.id = p.cliente_id;
 
-- LEFT JOIN: TODOS los clientes (20 filas, incluyendo Diego)
SELECT cl.nombre, p.id AS pedido_id
FROM clientes cl
LEFT JOIN pedidos p ON cl.id = p.cliente_id
WHERE p.id IS NULL;

Usa INNER JOIN cuando solo te interesan los registros que tienen relación en ambas tablas. Usa LEFT JOIN cuando necesitas todos los registros de la tabla izquierda, tengan o no relación. El caso más típico del LEFT JOIN es el patrón de "encontrar los que no tienen" que hemos visto a lo largo de este artículo.

Practica con LEFT JOIN

Usa el editor para probar LEFT JOIN y ver las filas sin correspondencia:

Simulador SQL
Ctrl+Enter para ejecutar

En el siguiente artículo veremos RIGHT JOIN, que es el espejo del LEFT JOIN.

Escrito por Eduardo Lázaro