MySQL 在多个外部连接的子句中放置条件

Posted

技术标签:

【中文标题】MySQL 在多个外部连接的子句中放置条件【英文标题】:MySQL placement of conditions in on-clauses of multiple outer joins 【发布时间】:2015-02-25 03:06:16 【问题描述】:

作为 In SQL / mysql, what is the difference between "ON" and "WHERE" in a join statement? 和 SQL join: where clause vs. on clause 的后续行动 - 将条件放在 on 子句与外连接中的 where 子句之间确实很重要。

但是,当有多个外连接时,哪个子句放置条件是否重要?

例如,这些会产生不同的结果吗?

select * from t1 left join t2 on t1.fid=t2.id and t2.col=val
                 left join t3 on t2.fid=t3.id;

对比:

select * from t1 left join t2 on t1.fid=t2.id
                 left join t3 on t2.fid=t3.id and t2.col=val;

【问题讨论】:

val: 那是什么? t1.val 还是别的什么? 【参考方案1】:

它们绝对不同。

第一个查询将只有满足t2.col=valt2

第二个查询将包括所有t2 行,并且仅在t2.col=val 时列出t3

【讨论】:

太好了,谢谢@Jasen。我的想法是,如果你想过滤最左边的表(和上) - 使用“where”,下一个表 - 使用最左边的“on”,然后是下一个“on”,等等所以过滤的顺序是:where, on #1, on #2 ... 是的,通常where 只提到起始表,如果您使用where 中其他表的列,则通常意味着该表的列上的is not nullleft join 变为一个inner join【参考方案2】:

查询不等价。反例很容易构建:

create table t1 (id int not null, val int not null);
create table t2 (id int not null, val int not null);
create table t3 (id int not null, val int not null);
insert into t1 (id, val) values (1,1);
insert into t2 (id, val) values (1,1);
insert into t3 (id, val) values (1,1);

select * from t1 
left join t2 
    on t1.id = t2.id 
    and t2.val = 2 
left join t3 
    on t2.id = t3.id;
+----+-----+------+------+------+------+
| id | val | id   | val  | id   | val  |
+----+-----+------+------+------+------+
|  1 |   1 | NULL | NULL | NULL | NULL |
+----+-----+------+------+------+------+

select * from t1 
left join t2 
    on t1.id = t2.id 
left join t3 
    on t2.id = t3.id 
    and t2.val = 2;
+----+-----+------+------+------+------+
| id | val | id   | val  | id   | val  |
+----+-----+------+------+------+------+
|  1 |   1 |    1 |    1 | NULL | NULL |
+----+-----+------+------+------+------+

【讨论】:

【参考方案3】:

这是查询之一:

select *
from t1 left join
     t2
     on t1.fid = t2.id left join
     t3
     on t2.fid = t3.id and t2.col = val;

是的,结果不同。如果您使用的是inner join,它们将是相同的,但left join 改变了一些东西——因为join 子句不会对行进行任何过滤。

我认为最简单的解释是t1t2 之间的连接将包括来自t1 的所有行以及来自t2 的所有匹配行——甚至包括t2.col <> val 的那些行。这些保留在结果集中,因为下一个 left join 不会将它们过滤掉。

实际上,第二个on 子句中的条件t2.col = val 不会影响结果集中的哪些行。如果存在匹配项,则来自t3 的行将基于第一个条件。如果没有匹配项,则来自t3 的行仍在结果集中——但t3 列将是NULL

在这个版本中:

select *
from t1 left join
     t2
     on t1.fid = t2.id  and t2.col = val left join
     t3
     on t2.fid = t3.id;

第一个连接从t1 获取所有行,并且仅从t2 获取匹配行,其中t2.col = val。然后第三个连接可以添加更多行。

注意:在某些情况下,两个查询肯定会返回相同的结果。但是,以下数据会产生不同的结果(假设 val = 0):

t1

fid
1

t2

fid   col
1     0
1     1

t3

id 
1

第二个on子句中带有条件的查询将返回:

1    1    0    1
1    1    1    NULL

第一个on子句中的条件查询将返回:

1    1    0    1

【讨论】:

以上是关于MySQL 在多个外部连接的子句中放置条件的主要内容,如果未能解决你的问题,请参考以下文章

如何在多个 Where 子句之间连接 MySQL

MySQL内连接(INNER JOIN)

SQL IN 子句 1000 项限制

如何在jquery中放置多个样式属性[重复]

你可以在救援修饰符中放置多个赋值吗?

如何在 mysqli 准备语句中使用多个内部连接和多个 WHERE 子句? [复制]