Příklad s tabulkami A a 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
Pokud chcete najít rodiče a jejich děti, proveďte INNER JOIN
:
SELECT id, parent.name AS parent
, pid, child.name AS child
FROM
parent INNER JOIN child
ON parent.id = child.pid
Výsledkem je, že každá shoda parent
id
uživatele z levé tabulky a child
's pid
z druhé tabulky se ve výsledku zobrazí jako řádek:
+----+--------+------+-------+
| id | parent | pid | child |
+----+--------+------+-------+
| 1 | Alex | 1 | Kate |
| 1 | Alex | 1 | Lia |
| 3 | Cath | 3 | Mary |
+----+--------+------+-------+
Výše uvedené neukazuje rodiče bez dětí (protože jejich id se neshodují s id dítěte, tak co uděláte? Místo toho provedete vnější spojení. Existují tři typy vnějších spojení, levé, pravé a úplné vnější spojení. Potřebujeme levý, protože chceme „extra“ řádky z levé tabulky (nadřazené):
SELECT id, parent.name AS parent
, pid, child.name AS child
FROM
parent LEFT JOIN child
ON parent.id = child.pid
Výsledkem je, že kromě předchozích zápasů jsou zobrazeni také všichni rodiče, kteří nemají shodu (čti:nemají dítě):
+----+--------+------+-------+
| 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 |
+----+--------+------+-------+
Kde se poděly všechny ty NULLs
pocházet z? No, MySQL (nebo jakýkoli jiný RDBMS, který můžete použít) nebude vědět, co tam dát, protože tito rodiče nemají žádnou shodu (dítě), takže neexistuje žádný pid
ani child.name
vyrovnat se s těmi rodiči. Takže vloží tuto speciální nehodnotu nazvanou NULLs
.
Chci říct, že tyto NULLs
jsou vytvořeny (ve výsledkové sadě) během LEFT OUTER JOIN
.
Pokud tedy chceme zobrazit pouze rodiče, kteří NEMAJÍ dítě, můžeme přidat WHERE child.pid IS NULL
do LEFT JOIN
výše. Položka WHERE
klauzule je vyhodnocena (kontrolována) po JOIN
je hotovo. Z výše uvedeného výsledku je tedy jasné, že pouze poslední tři řádky obsahují pid
je NULL se zobrazí:
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
Výsledek:
+----+--------+------+-------+
| id | parent | pid | child |
+----+--------+------+-------+
| 2 | Bill | NULL | NULL |
| 4 | Dale | NULL | NULL |
| 5 | Evan | NULL | NULL |
+----+--------+------+-------+
Nyní, co se stane, když přesuneme to IS NULL
zkontrolujte z WHERE
ke spojování ON
klauzule?
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
V tomto případě se databáze pokusí najít řádky ze dvou tabulek, které odpovídají těmto podmínkám. Tedy řádky, kde parent.id = child.pid
A child.pid IN NULL
. Nemůže však najít žádnou takovou shodu protože žádné child.pid
se může něčemu rovnat (1, 2, 3, 4 nebo 5) a zároveň být NULL!
Takže podmínka:
ON parent.id = child.pid
AND child.pid IS NULL
je ekvivalentní:
ON 1 = 0
což je vždy False
.
Proč tedy vrací VŠECHNY řádky z levé tabulky? Protože jde o LEVÉ PŘIPOJENÍ! A levá spojení vrátí řádky, které se shodují (v tomto případě žádné) a také řádky z levé tabulky, které se neshodují šek (v tomto případě vše ):
+----+--------+------+-------+
| id | parent | pid | child |
+----+--------+------+-------+
| 1 | Alex | NULL | NULL |
| 2 | Bill | NULL | NULL |
| 3 | Cath | NULL | NULL |
| 4 | Dale | NULL | NULL |
| 5 | Evan | NULL | NULL |
+----+--------+------+-------+
Doufám, že výše uvedené vysvětlení je jasné.
Vedlejší poznámka (není přímo spojena s vaší otázkou):Proč se proboha Pan
nezobrazí se v žádném z našich JOINů? Protože jeho pid
je NULLs
a NULL v (neběžné) logice SQL se nerovná ničemu, takže se nemůže shodovat s žádným z rodičovských id (které jsou 1, 2, 3, 4 a 5). I kdyby tam byla NULL, stále by se neshodovala, protože NULLs
nerovná se nic, dokonce ani NULLs
(je to skutečně velmi zvláštní logika!). Proto používáme speciální kontrolu IS NULL
a nikoli = NULL
zkontrolovat.
Takže se Pan
zobrazí se, pokud provedeme RIGHT JOIN
? Ano, bude! Protože RIGHT JOIN zobrazí všechny výsledky, které se shodují (první INNER JOIN, které jsme udělali) plus všechny řádky z tabulky RIGHT, které se neshodují (což je v našem případě jeden, (NULL, 'Pan')
řádek.
SELECT id, parent.name AS parent
, pid, child.name AS child
FROM
parent RIGHT JOIN child
ON parent.id = child.pid
Výsledek:
+------+--------+------+-------+
| id | parent | pid | child |
+---------------+------+-------+
| 1 | Alex | 1 | Kate |
| 1 | Alex | 1 | Lia |
| 3 | Cath | 3 | Mary |
| NULL | NULL | NULL | Pan |
+------+--------+------+-------+
Bohužel MySQL nemá FULL JOIN
. Můžete to zkusit v jiných RDBMS a zobrazí se:
+------+--------+------+-------+
| 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 |
+------+--------+------+-------+