以表 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 期间创建的(在结果集中)。
所以,如果我们只想显示没有孩子的父母,我们可以在上面的LEFT JOIN 中添加一个WHERE child.pid IS NULL。 在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 AND 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 |
+----+--------+------+-------+
希望上面的解释清楚。
旁注(与您的问题没有直接关系):为什么 Pan 没有出现在我们的任何 JOIN 中?因为他的pid 是NULL 并且SQL(不常见)逻辑中的NULL 不等于任何东西,所以它不能与任何父ID(即1、2、3、4 和5)匹配.即使那里有一个 NULL,它仍然不匹配,因为 NULL 不等于任何东西,甚至不等于 NULL 本身(这确实是一个非常奇怪的逻辑!)。这就是为什么我们使用特殊检查 IS NULL 而不是 = NULL 检查。
那么,如果我们执行RIGHT JOIN,Pan 会出现吗?是的,它会!因为 RIGHT JOIN 将显示所有匹配的结果(我们执行的第一个 INNER JOIN)以及 RIGHT 表中不匹配的所有行(在我们的例子中是一个,(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 |
+------+--------+------+-------+