sql >> Base de Datos >  >> RDS >> Oracle

Uso de IS NULL o IS NOT NULL en condiciones de unión - Pregunta de teoría

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   |
+------+--------+------+-------+