Combinación externa OUTER JOIN

La combinación interna solo conserva las filas para las que se ha encontrado coincidencia en la segunda tabla. La combinación externa funciona de otra manera: devuelve siempre todas las filas de una de las tablas o de las dos, y la mitad que falta la rellena con valores NULL.

Hay tres tipos de combinación externa: izquierda (LEFT), derecha (RIGHT) y completa (FULL). Es obligatorio indicar el tipo: escribir solo OUTER JOIN no está permitido y da un error de sintaxis. En cambio, la palabra OUTER es opcional: LEFT JOIN y LEFT OUTER JOIN significan lo mismo, y a partir de aquí usaremos la forma corta.

Combinación externa izquierda (LEFT OUTER JOIN)

Devuelve todas las filas de la tabla izquierda. Si una fila tiene coincidencia en la tabla derecha, las dos filas se unen; si no la tiene, las columnas de la tabla derecha se rellenan con NULL.

Como ejemplo, vamos a obtener de la base de datos el horario de timbres combinado con las clases correspondientes del horario de clases.

Datos de la tabla Timepair (horario de timbres):

idstart_pairend_pair
108:30:0009:15:00
209:20:0010:05:00
310:15:0011:00:00
411:05:0011:50:00
512:50:0013:35:00
613:40:0014:25:00
714:35:0015:20:00
815:25:0016:10:00

Datos de la tabla Schedule (horario de clases):

iddateclassnumber_pairteachersubjectclassroom
12019-09-01T00:00:00.000Z9111147
22019-09-01T00:00:00.000Z928213
32019-09-01T00:00:00.000Z934313
42019-09-02T00:00:00.000Z914313
52019-09-02T00:00:00.000Z922434
62019-09-02T00:00:00.000Z936535
72019-09-03T00:00:00.000Z915636
82019-09-03T00:00:00.000Z9213737
92019-09-03T00:00:00.000Z936838
102019-09-04T00:00:00.000Z919939
112019-09-04T00:00:00.000Z92101040
122019-09-04T00:00:00.000Z9331141
132019-09-05T00:00:00.000Z9131343
142019-09-05T00:00:00.000Z9211147
152019-09-05T00:00:00.000Z935636
162019-08-30T00:00:00.000Z912434
172019-08-30T00:00:00.000Z928213
182019-08-30T00:00:00.000Z936535
192019-08-30T00:00:00.000Z9410147
202019-09-03T00:00:00.000Z94101040
212019-08-30T00:00:00.000Z817953
222019-08-30T00:00:00.000Z827953
232019-08-30T00:00:00.000Z838238
242019-08-30T00:00:00.000Z8411143
252019-08-30T00:00:00.000Z858339
262019-09-01T00:00:00.000Z822434
272019-09-01T00:00:00.000Z836535
282019-09-01T00:00:00.000Z8412636
292019-09-01T00:00:00.000Z8513737
302019-09-02T00:00:00.000Z836838
312019-09-02T00:00:00.000Z847953
322019-09-03T00:00:00.000Z81101040
332019-09-03T00:00:00.000Z827953
342019-09-03T00:00:00.000Z837953
352019-09-04T00:00:00.000Z811114
362019-09-04T00:00:00.000Z8211242
372019-09-04T00:00:00.000Z8331343
382019-09-04T00:00:00.000Z848242
392019-09-04T00:00:00.000Z8511143
402019-09-05T00:00:00.000Z8211143
412019-09-03T00:00:00.000Z718242
422019-09-03T00:00:00.000Z724313
432019-09-03T00:00:00.000Z73151040
442019-09-02T00:00:00.000Z726535
MySQL 8.4
SELECT Timepair.id "timepair.id", start_pair, end_pair,
    Schedule.id "schedule.id", date, class, number_pair, teacher, subject, classroom
FROM Timepair
    LEFT JOIN Schedule ON Schedule.number_pair = Timepair.id;
timepair.idstart_pairend_pairschedule.iddateclassnumber_pairteachersubjectclassroom
108:30:0009:15:00412019-09-03T00:00:00.000Z718242
108:30:0009:15:00352019-09-04T00:00:00.000Z811114
108:30:0009:15:00322019-09-03T00:00:00.000Z81101040
108:30:0009:15:00212019-08-30T00:00:00.000Z817953
108:30:0009:15:00162019-08-30T00:00:00.000Z912434
108:30:0009:15:00132019-09-05T00:00:00.000Z9131343
108:30:0009:15:00102019-09-04T00:00:00.000Z919939
108:30:0009:15:0072019-09-03T00:00:00.000Z915636
108:30:0009:15:0042019-09-02T00:00:00.000Z914313
108:30:0009:15:0012019-09-01T00:00:00.000Z9111147
209:20:0010:05:00442019-09-02T00:00:00.000Z726535
209:20:0010:05:00422019-09-03T00:00:00.000Z724313
209:20:0010:05:00402019-09-05T00:00:00.000Z8211143
209:20:0010:05:00362019-09-04T00:00:00.000Z8211242
209:20:0010:05:00332019-09-03T00:00:00.000Z827953
209:20:0010:05:00262019-09-01T00:00:00.000Z822434
209:20:0010:05:00222019-08-30T00:00:00.000Z827953
209:20:0010:05:00172019-08-30T00:00:00.000Z928213
209:20:0010:05:00142019-09-05T00:00:00.000Z9211147
209:20:0010:05:00112019-09-04T00:00:00.000Z92101040
209:20:0010:05:0082019-09-03T00:00:00.000Z9213737
209:20:0010:05:0052019-09-02T00:00:00.000Z922434
209:20:0010:05:0022019-09-01T00:00:00.000Z928213
310:15:0011:00:00432019-09-03T00:00:00.000Z73151040
310:15:0011:00:00372019-09-04T00:00:00.000Z8331343
310:15:0011:00:00342019-09-03T00:00:00.000Z837953
310:15:0011:00:00302019-09-02T00:00:00.000Z836838
310:15:0011:00:00272019-09-01T00:00:00.000Z836535
310:15:0011:00:00232019-08-30T00:00:00.000Z838238
310:15:0011:00:00182019-08-30T00:00:00.000Z936535
310:15:0011:00:00152019-09-05T00:00:00.000Z935636
310:15:0011:00:00122019-09-04T00:00:00.000Z9331141
310:15:0011:00:0092019-09-03T00:00:00.000Z936838
310:15:0011:00:0062019-09-02T00:00:00.000Z936535
310:15:0011:00:0032019-09-01T00:00:00.000Z934313
411:05:0011:50:00382019-09-04T00:00:00.000Z848242
411:05:0011:50:00312019-09-02T00:00:00.000Z847953
411:05:0011:50:00282019-09-01T00:00:00.000Z8412636
411:05:0011:50:00242019-08-30T00:00:00.000Z8411143
411:05:0011:50:00202019-09-03T00:00:00.000Z94101040
411:05:0011:50:00192019-08-30T00:00:00.000Z9410147
512:50:0013:35:00392019-09-04T00:00:00.000Z8511143
512:50:0013:35:00292019-09-01T00:00:00.000Z8513737
512:50:0013:35:00252019-08-30T00:00:00.000Z858339
613:40:0014:25:00<NULL><NULL><NULL><NULL><NULL><NULL><NULL>
714:35:0015:20:00<NULL><NULL><NULL><NULL><NULL><NULL><NULL>
815:25:0016:10:00<NULL><NULL><NULL><NULL><NULL><NULL><NULL>

En el resultado aparecen los ocho timbres, tal y como promete la combinación izquierda. Pero han salido 47 filas, no 8: 44 filas para las clases de Schedule y otras 3 para los timbres sin clases.

La combinación no se limita a completar la tabla izquierda: recorre todos los pares de filas que cumplen la condición. Un mismo número de hora de clase aparece muchas veces en el horario de clases (en días distintos y en grupos distintos), y cada coincidencia genera una fila aparte. Si la clave de la tabla derecha no es única, el resultado tendrá más filas que la tabla izquierda.

Al final del resultado hay filas en las que todas las columnas de la clase son NULL. Son los timbres para los que no se ha encontrado ninguna clase: no tienen coincidencia, pero la fila de la tabla izquierda tiene que aparecer igualmente en el resultado.

Filas sin coincidencia

Sobre estos NULL se apoya el truco práctico más habitual: encontrar los registros que no tienen coincidencia. Basta con quedarse solo con las filas en las que la clave de la tabla derecha está vacía:

MySQL 8.4
SELECT Timepair.id, start_pair, end_pair
FROM Timepair
    LEFT JOIN Schedule ON Schedule.number_pair = Timepair.id
WHERE Schedule.number_pair IS NULL;
idstart_pairend_pair
613:40:0014:25:00
714:35:0015:20:00
815:25:0016:10:00

Quedan tres timbres a los que no se ha asignado ninguna clase.

Una combinación de la que solo se conservan las filas sin coincidencia se llama anti-join (ANTI JOIN). No tiene sintaxis propia: tanto en MySQL como en PostgreSQL se escribe justo así, con una combinación y la condición IS NULL.

Combinación externa derecha (RIGHT OUTER JOIN)

Es el reflejo de la izquierda: en el resultado entran obligatoriamente todas las filas de la tabla derecha, y las columnas que faltan de la izquierda se rellenan con NULL.

MySQL 8.4
SELECT Timepair.id "timepair.id", start_pair, end_pair,
    Schedule.id "schedule.id", date, class, number_pair, teacher, subject, classroom
FROM Timepair
    RIGHT JOIN Schedule ON Schedule.number_pair = Timepair.id;

El resultado tiene 44 filas, exactamente tantas como registros hay en Schedule, y ninguna fila con NULL. Es decir, coincide por completo con la combinación interna.

Esto ocurre porque cada clase hace referencia a un timbre que existe: en la tabla derecha simplemente no hay filas sin coincidencia. El tipo de combinación fija la regla, pero lo que acaba en el resultado lo deciden los datos.

Combinación externa completa (FULL OUTER JOIN)

Devuelve todas las filas de ambas tablas. Las filas que tienen coincidencia se unen, y las filas sin coincidencia de la tabla izquierda y de la derecha entran en el resultado con NULL en lugar de la otra mitad.

El resultado de una combinación completa se compone de tres partes:

  • las filas que entran en la combinación interna (INNER JOIN);
  • las filas de la tabla izquierda que no tienen coincidencia;
  • las filas de la tabla derecha que no tienen coincidencia.
MySQL 8.4
SELECT Timepair.id "timepair.id", start_pair, end_pair,
    Schedule.id "schedule.id", date, class, number_pair, teacher, subject, classroom
FROM Timepair
    FULL OUTER JOIN Schedule ON Schedule.number_pair = Timepair.id;

Con los datos de esta base, el resultado coincide con el de la combinación izquierda, las mismas 47 filas: aquí solo hay filas sin coincidencia en la izquierda.

MySQL no admite FULL OUTER JOIN, pero el mismo resultado se puede montar a mano: tomar la combinación izquierda y añadirle las filas de la tabla derecha que no tienen coincidencia.

MySQL 8.4
SELECT campos_tablas
FROM tabla_izquierda
    LEFT JOIN tabla_derecha ON tabla_derecha.clave = tabla_izquierda.clave

UNION ALL

SELECT campos_tablas
FROM tabla_izquierda
    RIGHT JOIN tabla_derecha ON tabla_derecha.clave = tabla_izquierda.clave
WHERE tabla_izquierda.clave IS NULL;
campos_tablas

Las columnas que entran en el resultado. En las dos partes debe haber el mismo número y en el mismo orden.

tabla_izquierda

La tabla izquierda de la combinación: de ella entran en el resultado también las filas sin coincidencia.

tabla_derecha

La tabla derecha de la combinación: de ella también entran en el resultado las filas sin coincidencia.

tabla_izquierda.clave

La columna de la tabla izquierda con la que se busca la coincidencia. En la segunda parte, IS NULL sobre ella deja solo las filas de la tabla derecha que no tienen coincidencia.

tabla_derecha.clave

La columna de la tabla derecha cuyo valor debe coincidir con la clave de la izquierda.

La condición de la segunda parte es obligatoria: sin ella, las filas con coincidencia aparecerían dos veces en el resultado.

Todos los tipos de combinación de tablas

Todas las filas de la tabla izquierda
Tabla izquierda
clave
1
2
3
Tabla derecha
clave
1
1
2
4
Resultado: 4 filas
izquierda.clave
derecha.clave
1
1
1
1
2
2
3
NULL
SELECT campos_tablas
FROM tabla_izquierda
    LEFT JOIN tabla_derecha ON tabla_derecha.clave = tabla_izquierda.clave

Ponte a prueba. La tabla izquierda tiene 8 filas. ¿Cuántas filas devolverá un LEFT JOIN con la tabla derecha?