了解使用不同 ON 子句的自连接
Understanding self-join with different ON clause
这是我的 table 结构:
// mytable
+----+---------+----------+
| id | related | subject |
+----+---------+----------+
| 1 | NULL | subject1 |
| 2 | 1 | |
+----+---------+----------+
有两个查询在我看来相同,但在测试中有不同的结果:
SELECT a.id, IFNULL(b.subject, a.subject)
FROM mytable a
LEFT JOIN mytable b ON a.id = b.related
+----+----------+
| 1 | subject1 |
| 2 | |
+----+----------+
SELECT a.id, IFNULL(b.subject, a.subject)
FROM mytable a
LEFT JOIN mytable b ON b.id = a.related
+----+----------+
| 1 | subject1 |
| 2 | subject1 |
+----+----------+
看,是self-join。那为什么ON a.id = b.related
和ON b.id = a.related
的结果不一样呢?
运行 你用 SELECT *
的查询来揭开一些谜团:
您的第一个查询:
SELECT *
FROM mytable a
LEFT JOIN mytable b ON a.id = b.related;
生成以下内容:
+----+---------+----------+--------+----------+----------+
| id | related | subject | id1 | related1 | subject1 |
+----+---------+----------+--------+----------+----------+
| 2 | 1 | <null> | <null> | <null> | <null> |
| 1 | <null> | subject1 | 2 | 1 | <null> |
+----+---------+----------+--------+----------+----------+
您的第二个查询:
SELECT *
FROM mytable a
LEFT JOIN mytable b ON b.id = a.related;
产生这个:
+----+---------+----------+--------+----------+----------+
| id | related | subject | id1 | related1 | subject1 |
+----+---------+----------+--------+----------+----------+
| 2 | 1 | <null> | 1 | <null> | subject1 |
| 1 | <null> | subject1 | <null> | <null> | <null> |
+----+---------+----------+--------+----------+----------+
您的第一个查询是将 ID 2
连接到相关的 2
。没有相关的 2
,并且由于 id 2
没有主题,您的 ifnull()
中没有 subject
。
您的第二个查询是将 1
关联到 ID 1
for a.id 2
。这从 b.id 1
中提取了一个 subject
,结果你得到了一个 id 2
的主题。
你真的必须在脑海中想象出 LEFT JOIN
在这里是如何工作的,以及它是如何受到你的 ON
子句的影响的。结果,您在这里有两个截然不同的查询。
两个查询都从 a
.
获取所有行
两个查询都在执行 outer 连接到 b
。
不同的是用于从 b."match" 中查找 "match" 的条件。
(查询可能看起来相同,但事实是它们有很大不同。)
作为演示,运行 查询如下:
SELECT a.id AS `a_id`
, a.related AS `a_related`
, a.subject AS `a_subject`
, b.id AS `b_id`
, b.related AS `b_related`
, b.subject AS `b_subject`
FROM mytable a
LEFT
JOIN mytable b
ON b.related = a.id
然后更改ON
子句
ON b.id = a.related
您可能还想重复这两个查询,删除 LEFT
关键字(使其成为内部联接而不是外部联接。)
查看外连接的一种方法...当 b
中的匹配行 未找到 时,b
中的虚拟行是发明。该虚拟行完全由 NULL 值组成,并且虚拟行连接到 a
,就好像它是匹配行一样。 (这不一定是数据库引擎实际执行的操作,但以这种方式思考可以让我们深入了解外部连接 returns 的结果。)
仔细查看查询结果,您将能够了解查询结果不同的原因。
a
和 b
指的是同一个 table 是一个特例。如果那是两个不同的 table,我们会看到相同的结果。真的没关系...对于查询,这是两个不同的来源,恰好引用相同的 table。不要让 a
和 b
指的是同一个 table 这一事实引起任何混淆。
这是我的 table 结构:
// mytable
+----+---------+----------+
| id | related | subject |
+----+---------+----------+
| 1 | NULL | subject1 |
| 2 | 1 | |
+----+---------+----------+
有两个查询在我看来相同,但在测试中有不同的结果:
SELECT a.id, IFNULL(b.subject, a.subject)
FROM mytable a
LEFT JOIN mytable b ON a.id = b.related
+----+----------+
| 1 | subject1 |
| 2 | |
+----+----------+
SELECT a.id, IFNULL(b.subject, a.subject)
FROM mytable a
LEFT JOIN mytable b ON b.id = a.related
+----+----------+
| 1 | subject1 |
| 2 | subject1 |
+----+----------+
看,是self-join。那为什么ON a.id = b.related
和ON b.id = a.related
的结果不一样呢?
运行 你用 SELECT *
的查询来揭开一些谜团:
您的第一个查询:
SELECT *
FROM mytable a
LEFT JOIN mytable b ON a.id = b.related;
生成以下内容:
+----+---------+----------+--------+----------+----------+
| id | related | subject | id1 | related1 | subject1 |
+----+---------+----------+--------+----------+----------+
| 2 | 1 | <null> | <null> | <null> | <null> |
| 1 | <null> | subject1 | 2 | 1 | <null> |
+----+---------+----------+--------+----------+----------+
您的第二个查询:
SELECT *
FROM mytable a
LEFT JOIN mytable b ON b.id = a.related;
产生这个:
+----+---------+----------+--------+----------+----------+
| id | related | subject | id1 | related1 | subject1 |
+----+---------+----------+--------+----------+----------+
| 2 | 1 | <null> | 1 | <null> | subject1 |
| 1 | <null> | subject1 | <null> | <null> | <null> |
+----+---------+----------+--------+----------+----------+
您的第一个查询是将 ID 2
连接到相关的 2
。没有相关的 2
,并且由于 id 2
没有主题,您的 ifnull()
中没有 subject
。
您的第二个查询是将 1
关联到 ID 1
for a.id 2
。这从 b.id 1
中提取了一个 subject
,结果你得到了一个 id 2
的主题。
你真的必须在脑海中想象出 LEFT JOIN
在这里是如何工作的,以及它是如何受到你的 ON
子句的影响的。结果,您在这里有两个截然不同的查询。
两个查询都从 a
.
两个查询都在执行 outer 连接到 b
。
不同的是用于从 b."match" 中查找 "match" 的条件。
(查询可能看起来相同,但事实是它们有很大不同。)
作为演示,运行 查询如下:
SELECT a.id AS `a_id`
, a.related AS `a_related`
, a.subject AS `a_subject`
, b.id AS `b_id`
, b.related AS `b_related`
, b.subject AS `b_subject`
FROM mytable a
LEFT
JOIN mytable b
ON b.related = a.id
然后更改ON
子句
ON b.id = a.related
您可能还想重复这两个查询,删除 LEFT
关键字(使其成为内部联接而不是外部联接。)
查看外连接的一种方法...当 b
中的匹配行 未找到 时,b
中的虚拟行是发明。该虚拟行完全由 NULL 值组成,并且虚拟行连接到 a
,就好像它是匹配行一样。 (这不一定是数据库引擎实际执行的操作,但以这种方式思考可以让我们深入了解外部连接 returns 的结果。)
仔细查看查询结果,您将能够了解查询结果不同的原因。
a
和 b
指的是同一个 table 是一个特例。如果那是两个不同的 table,我们会看到相同的结果。真的没关系...对于查询,这是两个不同的来源,恰好引用相同的 table。不要让 a
和 b
指的是同一个 table 这一事实引起任何混淆。