LEFT JOIN (nombre completo LEFT OUTER JOIN) es una unión en la que todas las filas de la tabla izquierda acaban en el resultado. Cuando no hay coincidencia a la derecha, las columnas del lado derecho son NULL.
Compáralo con INNER JOIN: ese descarta las filas sin coincidencia. LEFT JOIN las conserva, solo marca «nada a la derecha».
Analogía. Una profesora reparte exámenes. No todo el mundo entregó el suyo.
INNER JOIN — «muéstrame solo los alumnos que entregaron un examen, con su nota».
LEFT JOIN — «muéstrame a todos los alumnos; la nota si entregaron, en blanco si no».
Lo segundo es más a menudo lo que necesitas en la vida real — quieres ver quién se está quedando atrás.
Por qué importa LEFT JOIN
Escenarios clásicos:
- Una lista más información opcional, aunque falte. Todos los usuarios + cuántos pedidos tienen (incluido 0).
- Encontrar huérfanos — filas sin coincidencia. Usuarios sin pedidos. Productos que nadie compró nunca. Publicaciones sin comentarios.
- Contar huecos en los datos. Cuántas lecciones no tienen deberes, cuántas publicaciones no tienen comentarios.
INNER JOIN no sirve aquí — descarta las filas «sin hijos» de inmediato.
Sintaxis básica
SELECT columns
FROM table_A
LEFT JOIN table_B
ON A.key = B.key;
«Izquierda» es lo que sigue a FROM. «Derecha» es lo que sigue a LEFT JOIN. Recuerda la dirección: LEFT significa que el lado izquierdo se conserva por completo.
OUTER es opcional. LEFT JOIN y LEFT OUTER JOIN son lo mismo.
Ejemplo: un aula
students:
| id | name |
|---|
| 1 | Anna |
| 2 | Boris |
| 3 | Vera |
| 4 | Grisha |
grades (notas de examen):
| id | student_id | score |
|---|
| 10 | 1 | 5 |
| 11 | 1 | 4 |
| 12 | 2 | 3 |
Consulta: «cada alumno con sus notas, si las hay».
SELECT s.name, g.score
FROM students s
LEFT JOIN grades g ON s.id = g.student_id;
Resultado:
| name | score |
|---|
| Anna | 5 |
| Anna | 4 |
| Boris | 3 |
| Vera | NULL |
| Grisha | NULL |
Qué hay que observar:
- Los cuatro alumnos aparecen en el resultado. Ese es todo el sentido del LEFT JOIN.
- Anna tiene dos notas — dos filas.
- Boris tiene una — una fila.
- Vera y Grisha no tienen notas → una fila cada uno, con
NULL en score.
Si hubiéramos escrito INNER JOIN, solo quedarían Anna (dos filas) y Boris. Vera y Grisha se caerían — justamente las personas que le importan a la profesora.
Encontrar huérfanos — filas sin coincidencia
Truco clásico. Para encontrar los alumnos que no han hecho ningún examen, filtra por IS NULL:
SELECT s.name
FROM students s
LEFT JOIN grades g ON s.id = g.student_id
WHERE g.id IS NULL;
Resultado:
Lógica: LEFT JOIN conserva a cada alumno. Los que no tienen notas tienen NULL en g.id. WHERE g.id IS NULL se queda solo con ellos.
Este patrón (LEFT JOIN ... WHERE other.id IS NULL) es la forma estándar de encontrar «sin coincidencia». Se usa por todas partes: usuarios sin pedidos, publicaciones sin comentarios, productos que nadie compró.
No filtres la tabla derecha en WHERE
La trampa. Si quieres solo las notas de 5 y escribes:
SELECT s.name, g.score
FROM students s
LEFT JOIN grades g ON s.id = g.student_id
WHERE g.score = 5;
Resultado:
Vera y Grisha se cayeron. Porque para ellos WHERE g.score = 5 se evalúa como NULL = 5 (no TRUE) y quedan filtrados. El LEFT JOIN se convirtió de hecho en un INNER JOIN.
Si quieres conservar a todos los alumnos pero mostrar la nota solo cuando sea un 5 — la condición va en ON, no en WHERE:
SELECT s.name, g.score
FROM students s
LEFT JOIN grades g ON s.id = g.student_id AND g.score = 5;
Resultado:
| name | score |
|---|
| Anna | 5 |
| Boris | NULL |
| Vera | NULL |
| Grisha | NULL |
Ahora los cuatro están en el resultado; la nota aparece solo cuando es un 5. Una diferencia sutil pero crítica. Recuerda: las condiciones sobre la tabla derecha → ON; sobre la izquierda → WHERE.
INNER frente a LEFT — visualizado
Imagina dos tablas de un equipo de fútbol: players y goals (goles marcados). No todos los jugadores marcaron.
INNER JOIN → solo los jugadores que marcaron al menos una vez.
LEFT JOIN de players a goals → todos los jugadores; los que no marcaron tienen NULL en las columnas de goles.
LEFT JOIN + WHERE goals.id IS NULL → solo los jugadores que no marcaron en absoluto.
Tres preguntas distintas — tres consultas distintas.
Ejemplo más grande: comercio electrónico
users:
| id | name | signup_date |
|---|
| 1 | Anna | 2024-01-15 |
| 2 | Boris | 2024-02-01 |
| 3 | Vera | 2024-02-20 |
| 4 | Grisha | 2024-03-10 |
orders:
| id | user_id | amount | created_at |
|---|
| 50 | 1 | 500 | 2024-02-05 |
| 51 | 1 | 1500 | 2024-02-10 |
| 52 | 2 | 200 | 2024-02-25 |
Petición de negocio: «¿cuántos pedidos y cuánto gastó en total cada usuario registrado? Incluye a los que aún no han comprado nada».
SELECT
u.name,
COUNT(o.id) AS orders_count,
COALESCE(SUM(o.amount), 0) AS total_spent
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.name
ORDER BY total_spent DESC;
Resultado:
| name | orders_count | total_spent |
|---|
| Anna | 2 | 2000 |
| Boris | 1 | 200 |
| Vera | 0 | 0 |
| Grisha | 0 | 0 |
Puntos clave:
- Aparecen los 4 usuarios, incluso Vera y Grisha con cero pedidos.
COUNT(o.id) devuelve 0 para ellos (NULL no cuenta en COUNT).
SUM devolvería NULL para ellos, así que lo envolvemos en COALESCE(..., 0) — convertir NULL en 0 para una salida limpia.
Una consulta de panel de pan de cada día: «actividad por usuario, incluido el cero».
Errores habituales de principiante
1. WHERE sobre la tabla derecha, convirtiendo sin querer LEFT en INNER. El error más común y más traicionero. Los filtros sobre columnas del lado derecho → ON. Los filtros sobre columnas del lado izquierdo → WHERE.
2. Asustarse con los NULL del resultado. No son un problema — son una señal de «sin coincidencia a la derecha». Usa IS NULL, COALESCE, agregaciones que ignoran NULL, y vas bien.
3. Usar LEFT JOIN cuando hace falta INNER. Si el negocio dice «solo los usuarios que TIENEN pedidos» — eso es INNER JOIN. LEFT JOIN aquí da filas de más y puede ralentizar la consulta.
4. Apilar varios LEFT JOIN y perder el hilo. Las cadenas de A LEFT JOIN B LEFT JOIN C son delicadas — cada nueva condición es relativa a lo que ya hay. Para uniones complejas, constrúyelas de forma incremental.
5. RIGHT JOIN en lugar de intercambiar las tablas. RIGHT JOIN existe y hace la imagen reflejada. En la práctica casi nadie lo usa — simplemente intercambia las tablas y quédate con LEFT JOIN. Más fácil de leer.
6. COUNT(*) frente a COUNT(column) después de un LEFT JOIN. COUNT(*) cuenta todas las filas, incluidas aquellas donde el lado derecho es NULL. COUNT(o.id) cuenta solo las coincidencias reales. Con LEFT JOIN estos dan números distintos.
Resumen rápido
LEFT JOIN conserva TODAS las filas de la tabla izquierda.
- Si el lado derecho no tiene coincidencia — sus columnas son
NULL.
LEFT JOIN ... WHERE other.id IS NULL es la forma estándar de encontrar las filas sin coincidencia.
- Las condiciones sobre el lado derecho van en
ON; sobre el lado izquierdo, en WHERE.
OUTER es opcional; LEFT JOIN = LEFT OUTER JOIN.
- Más común en las tareas del mundo real que INNER, porque normalmente quieres ver la imagen completa, incluidos los ceros.
Pruébalo tú
Sobre users y orders de arriba:
- Lista TODOS los usuarios con el número de pedidos que tienen (incluido el cero).
- Encuentra los usuarios sin ningún pedido (patrón
IS NULL).
- Lista TODOS los usuarios con su pedido más reciente (si lo hay). Pista:
LEFT JOIN + MAX(created_at) + GROUP BY.
Cuando LEFT JOIN te encaja, se abre toda una clase de problemas que simplemente no se pueden resolver sin él.
LEFT JOIN(nombre completoLEFT OUTER JOIN) es una unión en la que todas las filas de la tabla izquierda acaban en el resultado. Cuando no hay coincidencia a la derecha, las columnas del lado derecho sonNULL.Compáralo con
INNER JOIN: ese descarta las filas sin coincidencia.LEFT JOINlas conserva, solo marca «nada a la derecha».Analogía. Una profesora reparte exámenes. No todo el mundo entregó el suyo.
INNER JOIN— «muéstrame solo los alumnos que entregaron un examen, con su nota».LEFT JOIN— «muéstrame a todos los alumnos; la nota si entregaron, en blanco si no».Lo segundo es más a menudo lo que necesitas en la vida real — quieres ver quién se está quedando atrás.
Por qué importa LEFT JOIN
Escenarios clásicos:
INNER JOINno sirve aquí — descarta las filas «sin hijos» de inmediato.Sintaxis básica
SELECT columns FROM table_A -- izquierda LEFT JOIN table_B -- derecha ON A.key = B.key;«Izquierda» es lo que sigue a
FROM. «Derecha» es lo que sigue aLEFT JOIN. Recuerda la dirección: LEFT significa que el lado izquierdo se conserva por completo.OUTERes opcional.LEFT JOINyLEFT OUTER JOINson lo mismo.Ejemplo: un aula
students:grades(notas de examen):Consulta: «cada alumno con sus notas, si las hay».
SELECT s.name, g.score FROM students s LEFT JOIN grades g ON s.id = g.student_id;Resultado:
Qué hay que observar:
NULLenscore.Si hubiéramos escrito
INNER JOIN, solo quedarían Anna (dos filas) y Boris. Vera y Grisha se caerían — justamente las personas que le importan a la profesora.Encontrar huérfanos — filas sin coincidencia
Truco clásico. Para encontrar los alumnos que no han hecho ningún examen, filtra por
IS NULL:SELECT s.name FROM students s LEFT JOIN grades g ON s.id = g.student_id WHERE g.id IS NULL;Resultado:
Lógica:
LEFT JOINconserva a cada alumno. Los que no tienen notas tienenNULLeng.id.WHERE g.id IS NULLse queda solo con ellos.Este patrón (
LEFT JOIN ... WHERE other.id IS NULL) es la forma estándar de encontrar «sin coincidencia». Se usa por todas partes: usuarios sin pedidos, publicaciones sin comentarios, productos que nadie compró.No filtres la tabla derecha en WHERE
La trampa. Si quieres solo las notas de 5 y escribes:
SELECT s.name, g.score FROM students s LEFT JOIN grades g ON s.id = g.student_id WHERE g.score = 5;Resultado:
Vera y Grisha se cayeron. Porque para ellos
WHERE g.score = 5se evalúa comoNULL = 5(no TRUE) y quedan filtrados. El LEFT JOIN se convirtió de hecho en un INNER JOIN.Si quieres conservar a todos los alumnos pero mostrar la nota solo cuando sea un 5 — la condición va en ON, no en WHERE:
SELECT s.name, g.score FROM students s LEFT JOIN grades g ON s.id = g.student_id AND g.score = 5;Resultado:
Ahora los cuatro están en el resultado; la nota aparece solo cuando es un 5. Una diferencia sutil pero crítica. Recuerda: las condiciones sobre la tabla derecha →
ON; sobre la izquierda →WHERE.INNER frente a LEFT — visualizado
Imagina dos tablas de un equipo de fútbol:
playersygoals(goles marcados). No todos los jugadores marcaron.INNER JOIN→ solo los jugadores que marcaron al menos una vez.LEFT JOINdeplayersagoals→ todos los jugadores; los que no marcaron tienenNULLen las columnas de goles.LEFT JOIN+WHERE goals.id IS NULL→ solo los jugadores que no marcaron en absoluto.Tres preguntas distintas — tres consultas distintas.
Ejemplo más grande: comercio electrónico
users:orders:Petición de negocio: «¿cuántos pedidos y cuánto gastó en total cada usuario registrado? Incluye a los que aún no han comprado nada».
SELECT u.name, COUNT(o.id) AS orders_count, COALESCE(SUM(o.amount), 0) AS total_spent FROM users u LEFT JOIN orders o ON u.id = o.user_id GROUP BY u.id, u.name ORDER BY total_spent DESC;Resultado:
Puntos clave:
COUNT(o.id)devuelve 0 para ellos (NULL no cuenta en COUNT).SUMdevolveríaNULLpara ellos, así que lo envolvemos enCOALESCE(..., 0)— convertir NULL en 0 para una salida limpia.Una consulta de panel de pan de cada día: «actividad por usuario, incluido el cero».
Errores habituales de principiante
1. WHERE sobre la tabla derecha, convirtiendo sin querer LEFT en INNER. El error más común y más traicionero. Los filtros sobre columnas del lado derecho →
ON. Los filtros sobre columnas del lado izquierdo →WHERE.2. Asustarse con los NULL del resultado. No son un problema — son una señal de «sin coincidencia a la derecha». Usa
IS NULL,COALESCE, agregaciones que ignoran NULL, y vas bien.3. Usar LEFT JOIN cuando hace falta INNER. Si el negocio dice «solo los usuarios que TIENEN pedidos» — eso es
INNER JOIN.LEFT JOINaquí da filas de más y puede ralentizar la consulta.4. Apilar varios LEFT JOIN y perder el hilo. Las cadenas de
A LEFT JOIN B LEFT JOIN Cson delicadas — cada nueva condición es relativa a lo que ya hay. Para uniones complejas, constrúyelas de forma incremental.5. RIGHT JOIN en lugar de intercambiar las tablas.
RIGHT JOINexiste y hace la imagen reflejada. En la práctica casi nadie lo usa — simplemente intercambia las tablas y quédate conLEFT JOIN. Más fácil de leer.6. COUNT(*) frente a COUNT(column) después de un LEFT JOIN.
COUNT(*)cuenta todas las filas, incluidas aquellas donde el lado derecho es NULL.COUNT(o.id)cuenta solo las coincidencias reales. Con LEFT JOIN estos dan números distintos.Resumen rápido
LEFT JOINconserva TODAS las filas de la tabla izquierda.NULL.LEFT JOIN ... WHERE other.id IS NULLes la forma estándar de encontrar las filas sin coincidencia.ON; sobre el lado izquierdo, enWHERE.OUTERes opcional;LEFT JOIN=LEFT OUTER JOIN.Pruébalo tú
Sobre
usersyordersde arriba:IS NULL).LEFT JOIN+MAX(created_at)+GROUP BY.Cuando LEFT JOIN te encaja, se abre toda una clase de problemas que simplemente no se pueden resolver sin él.