Módulo 3 · 03 | INNER, LEFT y RIGHT JOIN

MÓDULO 3 · CLASE 3

INNER, LEFT y RIGHT JOIN

Clase práctica. Cómo combinar tablas con cada tipo de JOIN, cuántas filas devuelve cada uno y los errores más comunes al usarlos en reportes de BI.

Antes de empezar

Usamos la base tablas_libro: 5 cursos (uno sin profesor: «SQL avanzado»), 4 profesores (uno sin cursos: Carlos), 6 alumnos y 8 inscripciones. Ejecutá USE tablas_libro; antes de cada ejemplo.

1. Resumen de los JOIN en MySQL

JOINQué devuelveFilas con cursos ⟶ profesores
INNER JOIN (o JOIN)Solo las filas que coinciden en ambas tablas4
LEFT JOINTodas las filas de la izquierda + coincidencias (o NULL)5
RIGHT JOINTodas las filas de la derecha + coincidencias (o NULL)5
CROSS JOINTodas las combinaciones posibles (producto cartesiano)5 × 4 = 20

Sintaxis general:

SELECT columnas FROM tabla_izquierda AS a [INNER | LEFT | RIGHT] JOIN tabla_derecha AS b ON a.columna_clave = b.columna_clave WHERE ... ORDER BY ...;

Cómo trabaja un JOIN, paso a paso

A · cursosID_CURSOTITULOID_PROFE1Programación PHP32Modelos abstracto…33SQL desde cero14Dibujo técnico25SQL avanzadoNULLB · profesoresID_PROFENOMBRE1Federico2Ana3Rosa4Carlos■ Sin pareja: «SQL avanzado» (ID_PROFE es NULL) y Carlos (no dicta cursos). INNER los descarta · LEFT conserva SQL avanzado · RIGHT conserva a Carlos.
Cada línea une un curso con el profesor cuyo ID_PROFE coincide (condición ON c.ID_PROFE = p.ID_PROFE).
  1. MySQL toma una fila de A (por ejemplo, «Programación PHP», con ID_PROFE = 3).
  2. Busca en B las filas que cumplen el ON: el profesor con ID_PROFE = 3 es Rosa.
  3. Combina la pareja en una fila de resultado: «Programación PHP – Rosa».
  4. Repite con cada fila de A. Rosa aparece dos veces porque dicta dos cursos.
  5. Decide qué hacer con las filas sin pareja («SQL avanzado» y Carlos). Es lo único que cambia entre un tipo de JOIN y otro: INNER las descarta, LEFT conserva las de A y RIGHT conserva las de B.

En los diagramas de Venn de esta clase, A es la tabla del FROM (izquierda), B la del JOIN (derecha), y la zona coloreada es lo que devuelve la consulta.

2. INNER JOIN: solo coincidencias

ABA = cursos · B = profesores
INNER JOIN: solo la intersección.
SELECT c.ID_CURSO, c.TITULO, p.NOMBRE, p.APELLIDOS FROM cursos AS c INNER JOIN profesores AS p ON c.ID_PROFE = p.ID_PROFE ORDER BY c.ID_CURSO;
ID_CURSOTITULONOMBREAPELLIDOS
1Programación PHPRosaHonrosa Pérez
2Modelos abstracto de datosRosaHonrosa Pérez
3SQL desde ceroFedericoGasco Daza
4Dibujo técnicoAnaSaura Trenzo

Devuelve 4 filas: «SQL avanzado» desaparece porque su ID_PROFE es NULL, y Carlos no aparece porque no dicta cursos.

Tres tablas a la vez: encadená un JOIN por cada relación. Así se ve qué alumno cursa qué curso, a través de la tabla puente:

SELECT a.NOMBRE, a.APELLIDOS, c.TITULO FROM alumnos AS a INNER JOIN alumnos_cursos AS ac ON ac.ID_ALUMNO = a.ID_ALUMNO INNER JOIN cursos AS c ON c.ID_CURSO = ac.ID_CURSO ORDER BY c.ID_CURSO, a.ID_ALUMNO; -- 8 filas, una por inscripción. Ej.: Pablo y Sergio en PHP y en SQL desde cero.

Atajo USING

Si la columna se llama igual en ambas tablas podés escribir JOIN alumnos_cursos USING (ID_ALUMNO) en lugar de ON .... JOIN a secas equivale a INNER JOIN.

3. LEFT JOIN: todo lo de la izquierda

ABA = cursos · B = profesores
LEFT JOIN: todo A, más lo que coincide en B.
SELECT c.TITULO, p.NOMBRE AS profesor FROM cursos AS c LEFT JOIN profesores AS p ON c.ID_PROFE = p.ID_PROFE ORDER BY c.ID_CURSO; -- 5 filas: las 4 anteriores + «SQL avanzado» con profesor NULL

Uso típico en BI: contar incluyendo los ceros. Inscriptos por curso, sin perder el curso vacío:

SELECT c.TITULO, COUNT(ac.ID_ALUMNO) AS inscriptos FROM cursos AS c LEFT JOIN alumnos_cursos AS ac ON ac.ID_CURSO = c.ID_CURSO GROUP BY c.ID_CURSO, c.TITULO ORDER BY c.ID_CURSO;
TITULOinscriptos
Programación PHP3
Modelos abstracto de datos1
SQL desde cero2
Dibujo técnico2
SQL avanzado0

COUNT(*) vs COUNT(columna)

Con LEFT JOIN, COUNT(*) cuenta también la fila de NULL y le daría 1 inscripto a «SQL avanzado». Usá COUNT(ac.ID_ALUMNO), que ignora los NULL.
ABA = cursos · B = profesores
LEFT JOIN + WHERE p.ID_PROFE IS NULL: solo lo de A sin pareja.

Anti-join: cursos que todavía no tienen profesor.

SELECT c.TITULO FROM cursos AS c LEFT JOIN profesores AS p ON c.ID_PROFE = p.ID_PROFE WHERE p.ID_PROFE IS NULL; -- SQL avanzado

4. RIGHT JOIN: todo lo de la derecha

ABA = cursos · B = profesores
RIGHT JOIN: todo B, más lo que coincide en A.

Es el espejo del LEFT JOIN. A RIGHT JOIN B da lo mismo que B LEFT JOIN A. Ejemplo: todos los profesores, dicten cursos o no.

SELECT p.NOMBRE, c.TITULO FROM cursos AS c RIGHT JOIN profesores AS p ON c.ID_PROFE = p.ID_PROFE ORDER BY p.ID_PROFE; -- 5 filas: Federico, Ana, Rosa (2 cursos) y Carlos con TITULO NULL -- Equivalente con LEFT JOIN (la forma más usada): SELECT p.NOMBRE, c.TITULO FROM profesores AS p LEFT JOIN cursos AS c ON c.ID_PROFE = p.ID_PROFE;

Buena práctica

La mayoría de los equipos escribe siempre LEFT JOIN y ordena las tablas para que la «principal» quede a la izquierda. Las consultas se leen más fácil.

5. El error más común: filtrar en WHERE después de un LEFT JOIN

-- A) Condición en ON: conserva TODOS los cursos → 5 filas SELECT c.TITULO, p.NOMBRE FROM cursos AS c LEFT JOIN profesores AS p ON p.ID_PROFE = c.ID_PROFE AND p.F_NACIMIENTO >= '1975-01-01'; -- Dibujo técnico (Ana nació en 1969) y SQL avanzado quedan con NULL -- B) Condición en WHERE: descarta las filas NULL → 3 filas SELECT c.TITULO, p.NOMBRE FROM cursos AS c LEFT JOIN profesores AS p ON p.ID_PROFE = c.ID_PROFE WHERE p.F_NACIMIENTO >= '1975-01-01';

En la versión B, NULL >= '1975-01-01' no es verdadero, así que «Dibujo técnico» y «SQL avanzado» desaparecen: el LEFT JOIN se comporta como un INNER JOIN. Si querés filtrar la tabla de la derecha sin perder filas de la izquierda, poné la condición en el ON.

6. La misma tabla dos veces

En eventos cada partido tiene un equipo local y uno visitante, y los dos apuntan a equipos. Para ver los dos nombres se une equipos dos veces con alias distintos (la misma técnica se usa en un self join, cuando una tabla se une consigo misma):

SELECT e.ID_JORNADA, l.EQUIPO AS local, v.EQUIPO AS visitante, e.RESULTADO FROM eventos AS e INNER JOIN equipos AS l ON l.ID_EQUIPO = e.LOCAL INNER JOIN equipos AS v ON v.ID_EQUIPO = e.VISITANTE WHERE l.EQUIPO = 'Celta' OR v.EQUIPO = 'Celta' ORDER BY e.ID_JORNADA; -- 10 partidos del Celta; las jornadas 8, 9 y 10 tienen RESULTADO NULL (sin disputar)

7. ¿Y FULL OUTER JOIN?

ABA = cursos · B = profesores
FULL OUTER: todo A y todo B (en MySQL se emula).

MySQL no tiene FULL OUTER JOIN. Se emula uniendo un LEFT y un RIGHT con UNION (que elimina duplicados):

SELECT c.TITULO, p.NOMBRE FROM cursos AS c LEFT JOIN profesores AS p ON p.ID_PROFE = c.ID_PROFE UNION SELECT c.TITULO, p.NOMBRE FROM cursos AS c RIGHT JOIN profesores AS p ON p.ID_PROFE = c.ID_PROFE; -- 6 filas: 4 parejas + «SQL avanzado | NULL» + «NULL | Carlos»

Chuleta visual: los 7 JOIN en MySQL

Referencia rápida con A = cursos y B = profesores. Los resultados son los de tablas_libro.

ABA = cursos · B = profesores

INNER JOIN

SELECT *
FROM cursos c
INNER JOIN profesores p
  ON c.ID_PROFE = p.ID_PROFE;

Solo coincidencias → 4 filas.

ABA = cursos · B = profesores

LEFT JOIN

SELECT *
FROM cursos c
LEFT JOIN profesores p
  ON c.ID_PROFE = p.ID_PROFE;

Todo A + coincidencias → 5 filas.

ABA = cursos · B = profesores

LEFT JOIN excluyente

SELECT *
FROM cursos c
LEFT JOIN profesores p
  ON c.ID_PROFE = p.ID_PROFE
WHERE p.ID_PROFE IS NULL;

Solo A sin pareja → 1 fila (SQL avanzado).

ABA = cursos · B = profesores

RIGHT JOIN

SELECT *
FROM cursos c
RIGHT JOIN profesores p
  ON c.ID_PROFE = p.ID_PROFE;

Todo B + coincidencias → 5 filas.

ABA = cursos · B = profesores

RIGHT JOIN excluyente

SELECT *
FROM cursos c
RIGHT JOIN profesores p
  ON c.ID_PROFE = p.ID_PROFE
WHERE c.ID_CURSO IS NULL;

Solo B sin pareja → 1 fila (Carlos).

ABA = cursos · B = profesores

FULL OUTER JOIN (emulado)

SELECT * FROM cursos c
LEFT JOIN profesores p
  ON c.ID_PROFE = p.ID_PROFE
UNION
SELECT * FROM cursos c
RIGHT JOIN profesores p
  ON c.ID_PROFE = p.ID_PROFE;

Todo A y todo B → 6 filas.

ABA = cursos · B = profesores

FULL OUTER excluyente (emulado)

SELECT * FROM cursos c
LEFT JOIN profesores p
  ON c.ID_PROFE = p.ID_PROFE
WHERE p.ID_PROFE IS NULL
UNION ALL
SELECT * FROM cursos c
RIGHT JOIN profesores p
  ON c.ID_PROFE = p.ID_PROFE
WHERE c.ID_CURSO IS NULL;

Solo filas sin pareja → 2 filas (SQL avanzado y Carlos).

Tip

En las versiones «excluyentes» chequeá con IS NULL una columna NOT NULL de la otra tabla (su clave primaria). Si usás una columna que puede ser NULL por naturaleza, vas a obtener resultados falsos.

8. Checklist antes de confiar en un JOIN

  • ¿Cuántas filas esperabas? Compará con SELECT COUNT(*) de la tabla principal.
  • ¿Uniste por la clave correcta (FK → PK)? Una condición incompleta multiplica filas: eventos CROSS JOIN equipos da 30 × 6 = 180 filas.
  • ¿Necesitás conservar filas sin coincidencia? Entonces LEFT JOIN, no INNER.
  • ¿Filtraste la tabla de la derecha en WHERE? Revisá si no convertiste el LEFT en INNER.
  • Usá alias cortos (c, p, ac) y calificá todas las columnas.

9. Práctica

  • Listá los profesores que no dictan ningún curso.
  • Mostrá cuántos alumnos tiene cada profesor (sumando todos sus cursos), incluyendo al que tiene 0.
  • Mostrá cada alumno con su cantidad de actuaciones destacadas, incluyendo a los que no tienen ninguna.
-- Soluciones -- 1) Resultado: Carlos SELECT p.NOMBRE, p.APELLIDOS FROM profesores AS p LEFT JOIN cursos AS c ON c.ID_PROFE = p.ID_PROFE WHERE c.ID_CURSO IS NULL; -- 2) Resultado: Rosa 4 · Ana 2 · Federico 2 · Carlos 0 SELECT p.NOMBRE, COUNT(ac.ID_ALUMNO) AS alumnos FROM profesores AS p LEFT JOIN cursos AS c ON c.ID_PROFE = p.ID_PROFE LEFT JOIN alumnos_cursos AS ac ON ac.ID_CURSO = c.ID_CURSO GROUP BY p.ID_PROFE, p.NOMBRE ORDER BY alumnos DESC, p.NOMBRE; -- 3) Resultado: Teresa 2 · Jeremias 1 · Sergio 1 · Pablo, Marta y Carmen 0 SELECT a.NOMBRE, COUNT(ad.ACTUACION) AS destacadas FROM alumnos AS a LEFT JOIN actuaciones_destacadas AS ad ON ad.ID_ALUMNO = a.ID_ALUMNO GROUP BY a.ID_ALUMNO, a.NOMBRE ORDER BY destacadas DESC;

Para seguir practicando online sin instalar nada: SQLBolt – Lección 6 (JOINs) y Lección 7 (OUTER JOINs).


Bibliografía y recursos online

Los recursos en inglés se pueden leer con el traductor del navegador.

Siguiente: Cuestionario del Módulo 3 y luego Módulo 4 · GROUP BY y funciones de agregación.

Scroll al inicio
¿Consultas? Escribinos