El síntoma: escribes LEFT JOIN, pero desaparecen filas de la tabla izquierda
Un LEFT JOIN conserva las filas de la tabla izquierda aunque no encuentre coincidencia a la derecha, rellenando con NULL las columnas del lado derecho. El problema aparece cuando una condición posterior en WHERE exige un valor concreto de esas columnas: las filas extendidas con NULL dejan de cumplir el filtro.
La consulta puede ser sintácticamente correcta y, aun así, comportarse como un INNER JOIN para esa condición.
Ejemplo: el filtro en WHERE elimina clientes
WITH clientes(id, nombre) AS (
VALUES (1, 'Ana'), (2, 'Luis'), (3, 'Marta')
), pedidos(id, cliente_id, estado) AS (
VALUES (101, 1, 'pagado'), (102, 2, 'pendiente')
)
SELECT c.nombre, p.estado
FROM clientes AS c
LEFT JOIN pedidos AS p
ON p.cliente_id = c.id
WHERE p.estado = 'pagado'
ORDER BY c.id;| nombre | estado |
|---|---|
| Ana | pagado |
Luis tiene un pedido pendiente y Marta no tiene pedido. Después del LEFT JOIN, la condición de WHERE descarta ambos casos porque p.estado = 'pagado' no resulta verdadera.
Si quieres conservar todos los clientes, filtra la coincidencia en ON
WITH clientes(id, nombre) AS (
VALUES (1, 'Ana'), (2, 'Luis'), (3, 'Marta')
), pedidos(id, cliente_id, estado) AS (
VALUES (101, 1, 'pagado'), (102, 2, 'pendiente')
)
SELECT c.nombre, p.estado
FROM clientes AS c
LEFT JOIN pedidos AS p
ON p.cliente_id = c.id
AND p.estado = 'pagado'
ORDER BY c.id;| nombre | estado |
|---|---|
| Ana | pagado |
| Luis | NULL |
| Marta | NULL |
No muevas condiciones entre ON y WHERE mecánicamente
- Define qué filas deben sobrevivir. ¿Quieres todos los clientes o solo clientes con un pedido pagado?
- Identifica qué condición define una coincidencia válida. Esa condición puede pertenecer a
ON. - Identifica qué condición filtra el resultado final. Esa condición puede pertenecer a
WHERE. - Prueba un caso sin coincidencia. Si no tienes una fila así en los datos de prueba, es fácil no detectar el problema.
Error frecuente: creer que ON y WHERE son intercambiables en un outer join
En INNER JOIN suelen coincidir; en LEFT JOIN pueden cambiar el resultado
En un outer join, las filas sin coincidencia se añaden con NULL después de evaluar la condición de unión y antes del filtrado final. Por eso una condición sobre la tabla derecha puede conservar o eliminar esas filas según dónde se aplique.
Práctica correctiva
Conserva todos los clientes y muestra solo pedidos pendientes
Partiendo de las mismas CTE, devuelve Ana, Luis y Marta. La columna de pedido debe tener valor solo cuando exista un pedido pendiente.
p.estado = 'pendiente' dentro de ON.Solución razonada
WITH clientes(id, nombre) AS (
VALUES (1, 'Ana'), (2, 'Luis'), (3, 'Marta')
), pedidos(id, cliente_id, estado) AS (
VALUES (101, 1, 'pagado'), (102, 2, 'pendiente')
)
SELECT c.nombre, p.estado
FROM clientes AS c
LEFT JOIN pedidos AS p
ON p.cliente_id = c.id
AND p.estado = 'pendiente'
ORDER BY c.id;La tabla izquierda sigue definiendo quién debe aparecer. La condición de estado solo decide qué filas del lado derecho pueden emparejarse.
Qué debes recordar
- LEFT JOIN conserva las filas izquierdas antes del filtro final.
- Un WHERE sobre columnas derechas puede eliminar las filas extendidas con NULL.
- ON define coincidencias; WHERE filtra el resultado ya unido.
- Decide primero qué filas deben sobrevivir y después coloca cada condición.