Ejemplo con tablas A y B:
A (parent) B (child)
============ =============
id | name pid | name
------------ -------------
1 | Alex 1 | Kate
2 | Bill 1 | Lia
3 | Cath 3 | Mary
4 | Dale NULL | Pan
5 | Evan
Si desea encontrar padres y sus hijos, haga un INNER JOIN
:
SELECT id, parent.name AS parent
, pid, child.name AS child
FROM
parent INNER JOIN child
ON parent.id = child.pid
El resultado es que cada coincidencia de un parent
id
de de la tabla de la izquierda y un child
's pid
de la segunda tabla se mostrará como una fila en el resultado:
+----+--------+------+-------+
| id | parent | pid | child |
+----+--------+------+-------+
| 1 | Alex | 1 | Kate |
| 1 | Alex | 1 | Lia |
| 3 | Cath | 3 | Mary |
+----+--------+------+-------+
Ahora, lo anterior no muestra a los padres sin hijos (porque sus identificaciones no coinciden con las identificaciones de los niños, así que, ¿qué debe hacer? En su lugar, realiza una combinación externa. Hay tres tipos de combinaciones externas, la izquierda, la derecha y la combinación externa completa. Necesitamos la izquierda ya que queremos las filas "extra" de la tabla izquierda (padre):
SELECT id, parent.name AS parent
, pid, child.name AS child
FROM
parent LEFT JOIN child
ON parent.id = child.pid
El resultado es que, además de las coincidencias anteriores, también se muestran todos los padres que no tienen una coincidencia (léase:no tienen un hijo):
+----+--------+------+-------+
| id | parent | pid | child |
+----+--------+------+-------+
| 1 | Alex | 1 | Kate |
| 1 | Alex | 1 | Lia |
| 3 | Cath | 3 | Mary |
| 2 | Bill | NULL | NULL |
| 4 | Dale | NULL | NULL |
| 5 | Evan | NULL | NULL |
+----+--------+------+-------+
¿De dónde salieron todos esos NULLs
¿viene de? Bueno, MySQL (o cualquier otro RDBMS que pueda usar) no sabrá qué poner allí ya que estos padres no coinciden (niño), por lo que no hay pid
ni child.name
para coincidir con esos padres. Entonces, pone este no valor especial llamado NULLs
.
Mi punto es que estos NULLs
se crean (en el conjunto de resultados) durante LEFT OUTER JOIN
.
Entonces, si queremos mostrar solo a los padres que NO tienen un hijo, podemos agregar un WHERE child.pid IS NULL
a la LEFT JOIN
sobre. El WHERE
la cláusula se evalúa (marca) después de JOIN
está hecho. Entonces, está claro del resultado anterior que solo las últimas tres filas donde el pid
es NULL se mostrará:
SELECT id, parent.name AS parent
, pid, child.name AS child
FROM
parent LEFT JOIN child
ON parent.id = child.pid
WHERE child.pid IS NULL
Resultado:
+----+--------+------+-------+
| id | parent | pid | child |
+----+--------+------+-------+
| 2 | Bill | NULL | NULL |
| 4 | Dale | NULL | NULL |
| 5 | Evan | NULL | NULL |
+----+--------+------+-------+
Ahora, ¿qué sucede si movemos ese IS NULL
comprobar desde el WHERE
a la unión ON
cláusula?
SELECT id, parent.name AS parent
, pid, child.name AS child
FROM
parent LEFT JOIN child
ON parent.id = child.pid
AND child.pid IS NULL
En este caso, la base de datos intenta encontrar filas de las dos tablas que coincidan con estas condiciones. Es decir, filas donde parent.id = child.pid
Y child.pid IN NULL
. Pero no puede encontrar ninguna coincidencia porque no child.pid
puede ser igual a algo (1, 2, 3, 4 o 5) y ser NULL al mismo tiempo!
Entonces, la condición:
ON parent.id = child.pid
AND child.pid IS NULL
es equivalente a:
ON 1 = 0
que siempre es False
.
Entonces, ¿por qué devuelve TODAS las filas de la tabla de la izquierda? ¡Porque es un INGRESO IZQUIERDO! Y las uniones izquierdas devuelven filas que coinciden (ninguna en este caso) y también filas de la tabla de la izquierda que no coinciden el cheque (todo en este caso ):
+----+--------+------+-------+
| id | parent | pid | child |
+----+--------+------+-------+
| 1 | Alex | NULL | NULL |
| 2 | Bill | NULL | NULL |
| 3 | Cath | NULL | NULL |
| 4 | Dale | NULL | NULL |
| 5 | Evan | NULL | NULL |
+----+--------+------+-------+
Espero que la explicación anterior sea clara.
Nota al margen (no directamente relacionada con su pregunta):¿Por qué diablos no Pan
aparecer en ninguno de nuestros JOINs? Porque su pid
es NULL
y NULL en la lógica (no común) de SQL no es igual a nada, por lo que no puede coincidir con ninguna de las identificaciones principales (que son 1,2,3,4 y 5). Incluso si hubiera un NULL allí, aún no coincidiría porque NULLs
no es igual a nada, ni siquiera a NULLs
en sí mismo (¡es una lógica muy extraña, de hecho!). Por eso usamos la verificación especial IS NULL
y no un = NULL
verificar.
Entonces, Pan
aparecer si hacemos RIGHT JOIN
? ¡Sí, lo hará! Porque un RIGHT JOIN mostrará todos los resultados que coincidan (el primer INNER JOIN que hicimos) más todas las filas de la tabla RIGHT que no coincidan (que en nuestro caso es uno, el (NULL, 'Pan')
fila.
SELECT id, parent.name AS parent
, pid, child.name AS child
FROM
parent RIGHT JOIN child
ON parent.id = child.pid
Resultado:
+------+--------+------+-------+
| id | parent | pid | child |
+---------------+------+-------+
| 1 | Alex | 1 | Kate |
| 1 | Alex | 1 | Lia |
| 3 | Cath | 3 | Mary |
| NULL | NULL | NULL | Pan |
+------+--------+------+-------+
Desafortunadamente, MySQL no tiene FULL JOIN
. Puede probarlo en otros RDBMS y mostrará:
+------+--------+------+-------+
| id | parent | pid | child |
+------+--------+------+-------+
| 1 | Alex | 1 | Kate |
| 1 | Alex | 1 | Lia |
| 3 | Cath | 3 | Mary |
| 2 | Bill | NULL | NULL |
| 4 | Dale | NULL | NULL |
| 5 | Evan | NULL | NULL |
| NULL | NULL | NULL | Pan |
+------+--------+------+-------+