Oracle
 sql >> Baza danych >  >> RDS >> Oracle

Używanie IS NULL lub IS NOT NULL na warunkach przyłączenia — pytanie teoretyczne

Przykład z tabelami A i 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  

Jeśli chcesz znaleźć rodziców i ich dzieci, wykonaj INNER JOIN :

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

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

Rezultat jest taki, że każde dopasowanie rodzica id z lewej tabeli i dziecko pid z drugiej tabeli pokaże się jako wiersz w wyniku:

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

Teraz powyższe nie pokazuje rodziców bez dzieci (ponieważ ich identyfikatory nie pasują do identyfikatorów dzieci, więc co robisz? Zamiast tego wykonujesz sprzężenie zewnętrzne. Istnieją trzy typy sprzężeń zewnętrznych, lewe, prawe i pełne złącze zewnętrzne. Potrzebujemy lewego, ponieważ chcemy „dodatkowych” wierszy z lewej tabeli (rodzica):

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

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

Powoduje to, że oprócz poprzednich dopasowań, wszyscy rodzice, którzy nie mają dopasowania (czytaj:nie mają dziecka) są również pokazani:

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

Gdzie się podziały te wszystkie NULL pochodzić z? Cóż, MySQL (lub jakikolwiek inny RDBMS, którego możesz użyć) nie będzie wiedział, co tam umieścić, ponieważ ci rodzice nie mają dopasowania (dziecko), więc nie ma pid ani nazwa.dziecka pasować do tych rodziców. Tak więc umieszcza tę specjalną wartość niebędącą wartością o nazwie NULL .

Chodzi mi o to, że te NULL są tworzone (w zestawie wyników) podczas LEFT OUTER JOIN .

Tak więc, jeśli chcemy pokazać tylko tych rodziców, którzy NIE mają dziecka, możemy dodać GDZIE child.pid JEST NULL do LEFT JOIN nad. GDZIE klauzula jest oceniana (sprawdzana) po JOIN skończone. Tak więc z powyższego wyniku jasno wynika, że ​​tylko ostatnie trzy wiersze, w których pid zostanie wyświetlone NULL:

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

Wynik:

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

A teraz, co się stanie, jeśli przeniesiemy, że IS NULL sprawdź z GDZIE do dołączania ON klauzula?

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

W takim przypadku baza danych próbuje znaleźć wiersze z dwóch tabel, które spełniają te warunki. To znaczy wiersze, w których parent.id =child.pid ORAZ child.pid IN NULL . Ale może znaleźć brak takiego dopasowania ponieważ nie ma child.pid może być równe czemuś (1, 2, 3, 4 lub 5) i jednocześnie być NULL!

Tak więc warunek:

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

jest równoważne z:

ON   1 = 0

co zawsze jest Fałsz .

Dlaczego więc zwraca WSZYSTKIE wiersze z lewej tabeli? Ponieważ jest to LEWE DOŁĄCZENIE! A sprzężenia lewe zwracają pasujące wiersze (w tym przypadku brak) a także wiersze z lewej tabeli, które nie pasują czek (wszystkie w tym przypadku ):

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

Mam nadzieję, że powyższe wyjaśnienie jest jasne.

Uwaga poboczna (niezwiązana bezpośrednio z Twoim pytaniem):Dlaczego u licha nie Pan nie pojawiają się w żadnym z naszych JOIN? Ponieważ jego pid jest NULL i NULL w (nie wspólnej) logice SQL nie jest równe niczemu, więc nie może się zgadzać z żadnym z nadrzędnych identyfikatorów (czyli 1,2,3,4 i 5). Nawet gdyby był tam NULL, nadal nie pasowałby, ponieważ NULL nie równa się niczemu, nawet NULL (to naprawdę bardzo dziwna logika!). Dlatego używamy specjalnego sprawdzenia IS NULL a nie =NULL sprawdź.

Czy więc Pan? pokaż się, jeśli wykonamy RIGHT JOIN ? Tak, to będzie! Ponieważ RIGHT JOIN pokaże wszystkie pasujące wyniki (pierwsze INNER JOIN, które zrobiliśmy) oraz wszystkie wiersze z tabeli RIGHT, które nie pasują (co w naszym przypadku jest jednym, (NULL, 'Pan') wiersz.

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

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

Wynik:

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

Niestety, MySQL nie ma FULL JOIN . Możesz go wypróbować w innych systemach RDBMS, a pokaże się:

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


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. BIN_TO_NUM() Funkcja w Oracle

  2. Problem z hibernacją z wyzwalaczem Oracle do generowania identyfikatora z sekwencji

  3. Zapytanie o sprawdzenie rozmiaru tabeli w bazie danych Oracle

  4. Jak wyczyścić pulę połączeń ODP.NET w przypadku błędów połączenia?

  5. PLS-00428:w tej instrukcji SELECT oczekiwana jest klauzula INTO