Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Verwendung von IS NULL oder IS NOT NULL bei Join-Bedingungen – Theoriefrage

Beispiel mit den Tabellen A und 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  

Wenn Sie Eltern und ihre Kinder finden möchten, führen Sie einen INNER JOIN durch :

SELECT id,  parent.name AS parent
     , pid, child.name  AS child

FROM
        parent  INNER JOIN  child
  ON   parent.id     =    child.pid

Ergebnis ist, dass jede Übereinstimmung eines parent id von aus der linken Tabelle und ein child 's pid aus der zweiten Tabelle wird im Ergebnis als Zeile angezeigt:

+----+--------+------+-------+
| id | parent | pid  | child | 
+----+--------+------+-------+
|  1 | Alex   |   1  | Kate  |
|  1 | Alex   |   1  | Lia   |
|  3 | Cath   |   3  | Mary  |
+----+--------+------+-------+

Nun, das Obige zeigt keine Eltern ohne Kinder (weil ihre IDs keine Übereinstimmung mit den IDs der Kinder haben, was tun Sie also? Sie führen stattdessen einen äußeren Join aus. Es gibt drei Arten von äußeren Joins, den linken, den rechten und der vollständige äußere Join. Wir brauchen den linken, da wir die "zusätzlichen" Zeilen aus der linken Tabelle (Elternteil) haben wollen:

SELECT id,  parent.name AS parent
     , pid, child.name  AS child

FROM
        parent  LEFT JOIN  child
  ON   parent.id    =    child.pid

Das Ergebnis ist, dass neben früheren Übereinstimmungen auch alle Eltern angezeigt werden, die keine Übereinstimmung haben (sprich:kein Kind haben):

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

Woher kommen all diese NULL komme aus? Nun, MySQL (oder jedes andere RDBMS, das Sie verwenden) wird nicht wissen, was es dort einfügen soll, da diese Eltern keine Übereinstimmung (Kind) haben, also gibt es kein pid noch child.name mit diesen Eltern zusammenzubringen. Also setzt es diesen speziellen Nicht-Wert namens NULL .

Mein Punkt ist, dass diese NULLs werden (in der Ergebnismenge) während des LEFT OUTER JOIN erstellt .

Wenn wir also nur die Eltern anzeigen möchten, die KEIN Kind haben, können wir ein WHERE child.pid IS NULL hinzufügen zum LEFT JOIN Oben. Das WHERE -Klausel wird nach dem JOIN ausgewertet (überprüft). ist fertig. Aus dem obigen Ergebnis geht also hervor, dass nur die letzten drei Zeilen die pid enthalten NULL ist, wird angezeigt:

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

Ergebnis:

+----+--------+------+-------+
| id | parent | pid  | child | 
+----+--------+------+-------+
|  2 | Bill   | NULL | NULL  |
|  4 | Dale   | NULL | NULL  |
|  5 | Evan   | NULL | NULL  |
+----+--------+------+-------+

Was passiert nun, wenn wir IS NULL verschieben überprüfen Sie von WHERE zum Beitritt ON Klausel?

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

In diesem Fall versucht die Datenbank, Zeilen aus den beiden Tabellen zu finden, die diesen Bedingungen entsprechen. Das heißt, Zeilen mit parent.id = child.pid UND child.pid IN NULL . Aber es kann keine solche Übereinstimmung finden weil keine child.pid kann gleich etwas sein (1, 2, 3, 4 oder 5) und gleichzeitig NULL sein!

Also, die Bedingung:

ON   parent.id    =    child.pid
AND  child.pid IS NULL

entspricht:

ON   1 = 0

was immer False ist .

Warum gibt es also ALLE Zeilen aus der linken Tabelle zurück? Weil es ein LEFT JOIN ist! Und Linksverknüpfungen geben Zeilen zurück, die übereinstimmen (in diesem Fall keine) und auch Zeilen aus der linken Tabelle, die nicht übereinstimmen der Scheck (alle in diesem Fall ):

+----+--------+------+-------+
| id | parent | pid  | child | 
+----+--------+------+-------+
|  1 | Alex   | NULL | NULL  |
|  2 | Bill   | NULL | NULL  |
|  3 | Cath   | NULL | NULL  |
|  4 | Dale   | NULL | NULL  |
|  5 | Evan   | NULL | NULL  |
+----+--------+------+-------+

Ich hoffe, die obige Erklärung ist klar.

Nebenbemerkung (nicht direkt mit Ihrer Frage verbunden):Warum um alles in der Welt Pan nicht in keinem unserer JOINs auftauchen? Weil seine pid ist NULL und NULL in der (nicht gemeinsamen) Logik von SQL ist nicht gleich irgendetwas, also kann es nicht mit einer der übergeordneten IDs (die 1,2,3,4 und 5 sind) übereinstimmen. Selbst wenn dort eine NULL wäre, würde es immer noch nicht passen, weil NULL ist mit nichts gleich, nicht einmal mit NULL selbst (es ist in der Tat eine sehr seltsame Logik!). Deshalb verwenden wir die spezielle Prüfung IS NULL und kein = NULL prüfen.

Also Pan erscheinen, wenn wir einen RIGHT JOIN machen ? Ja, es wird! Weil ein RIGHT JOIN alle Ergebnisse anzeigt, die übereinstimmen (der erste INNER JOIN, den wir gemacht haben) plus alle Zeilen aus der RIGHT-Tabelle, die nicht übereinstimmen (was in unserem Fall einer ist, der (NULL, 'Pan') Zeile.

SELECT id,  parent.name AS parent
     , pid, child.name  AS child

FROM
        parent  RIGHT JOIN  child
  ON   parent.id     =    child.pid

Ergebnis:

+------+--------+------+-------+
| id   | parent | pid  | child | 
+---------------+------+-------+
|   1  | Alex   |   1  | Kate  |
|   1  | Alex   |   1  | Lia   |
|   3  | Cath   |   3  | Mary  |
| NULL | NULL   | NULL | Pan   |
+------+--------+------+-------+

Leider hat MySQL keinen FULL JOIN . Sie können es in anderen RDBMSs ausprobieren, und es wird angezeigt:

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