例如用表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
如果你想找到父母和他們的孩子,你做一個INNER JOIN
:
SELECT id, parent.name AS parent
, pid, child.name AS child
FROM
parent INNER JOIN child
ON parent.id = child.pid
結果是,一個每場比賽parent
的id
從左邊的表和child
的pid
從第二個表中將顯示爲結果中的一行:
+----+--------+------+-------+
| id | parent | pid | child |
+----+--------+------+-------+
| 1 | Alex | 1 | Kate |
| 1 | Alex | 1 | Lia |
| 3 | Cath | 3 | Mary |
+----+--------+------+-------+
現在,上面沒有顯示父母沒有孩子(因爲它們的ID沒有在孩子的ID的比賽,所以你會怎麼做?你做一個外部連接。有三種類型的外連接,即左連接,右連接和全連接。我們需要左邊一個,因爲我們希望左表(父)的「額外」的行:
SELECT id, parent.name AS parent
, pid, child.name AS child
FROM
parent LEFT JOIN child
ON parent.id = child.pid
結果是,除了以前的比賽中,沒有一場比賽所有的父母(讀:沒有孩子)也顯示:
+----+--------+------+-------+
| 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
從哪裏來?那麼,MySQL(或任何其他您可能使用的RDBMS)將不知道該放什麼,因爲這些父母沒有匹配(孩子),所以沒有pid
和child.name
與父母匹配。所以,它把這個特殊的非價值稱爲NULL
。
我的意思是,這些NULLs
在LEFT OUTER JOIN
期間創建(在結果集中)。
所以,如果我們想只顯示沒有一個孩子家長,我們可以添加一個WHERE child.pid IS NULL
上述LEFT JOIN
。 在JOIN
完成後,對WHERE
子句進行評估(檢查)。所以,這是從上面的結果清楚地表明只有最後三行在pid
爲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
結果:
+----+--------+------+-------+
| id | parent | pid | child |
+----+--------+------+-------+
| 2 | Bill | NULL | NULL |
| 4 | Dale | NULL | NULL |
| 5 | Evan | NULL | NULL |
+----+--------+------+-------+
現在,如果我們繼續會發生什麼IS NULL
從WHERE
檢查加入ON
子句?
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
在這種情況下,數據庫嘗試從符合這些條件的兩個表中查找行。也就是說,其中行parent.id = child.pid
和child.pid IN NULL
。但它可以找到沒有這樣的匹配,因爲沒有child.pid
可以等於某事(1,2,3,4或5)並且同時爲NULL!
所以,條件:
ON parent.id = child.pid
AND child.pid IS NULL
等同於:
ON 1 = 0
這始終是False
。
那麼,爲什麼它會返回左表中的所有行? 因爲這是一個左連接!和左連接返回匹配(在這種情況下沒有)左表也行不匹配檢查(所有在這種情況下)行:
+----+--------+------+-------+
| id | parent | pid | child |
+----+--------+------+-------+
| 1 | Alex | NULL | NULL |
| 2 | Bill | NULL | NULL |
| 3 | Cath | NULL | NULL |
| 4 | Dale | NULL | NULL |
| 5 | Evan | NULL | NULL |
+----+--------+------+-------+
希望以上解釋清楚。
阿里納斯(不直接關係到你的問題):爲什麼地球上沒有在我們的JOIN不Pan
顯示出來?因爲他的pid
是NULL
,並且SQL的(不常見)邏輯中的NULL不等於任何東西,所以它不能與任何父id(它們是1,2,3,4和5)匹配。即使在那裏有NULL,它仍然不會匹配,因爲NULL
不等於任何東西,甚至不是NULL
本身(這確實是一個非常奇怪的邏輯!)。這就是爲什麼我們使用特殊檢查IS NULL
而不是= NULL
檢查。
那麼,如果我們做了RIGHT JOIN
,那麼Pan
會出現嗎?是的,它會!由於右連接將顯示所有結果那場比賽(第一INNER JOIN我們所做的)加上右表中的所有行不匹配(在我們的例子是一個,在(NULL, 'Pan')
行。
SELECT id, parent.name AS parent
, pid, child.name AS child
FROM
parent RIGHT JOIN child
ON parent.id = child.pid
結果:
+------+--------+------+-------+
| id | parent | pid | child |
+---------------+------+-------+
| 1 | Alex | 1 | Kate |
| 1 | Alex | 1 | Lia |
| 3 | Cath | 3 | Mary |
| NULL | NULL | NULL | Pan |
+------+--------+------+-------+
不幸的是,MySQL沒有FULL JOIN
。你可以嘗試在其他RDBMS,它會顯示:
+------+--------+------+-------+
| 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 |
+------+--------+------+-------+
你可以通過在'LEFT JOIN'和'RIGHT JOIN'之間建立一個'NULL'來僞造一個'FULL JOIN'。這有一些限制 - 例如,你不能更新或刪除 - 可能比它的價值更麻煩。 – Duncan