左连接后过滤与左连接预过滤数据集是否等价?两款SQL对比
这两个SQL语句完全不等价!
核心差异在于过滤条件的执行时机——是在左连接之前过滤右表,还是在左连接之后过滤整个结果集。咱们一步步拆解来看:
语句1的实际效果(本质是内连接)
select a.accountid, b.attribute1 from account as a left join dataset1 as b on a.accountid= b.accountid where b.attribute2= 'TEST';
左连接的本意是保留左表account的所有行,哪怕右表dataset1没有匹配的accountid(此时b的所有列都会是NULL)。但这里在where子句里加了b.attribute2='TEST'——NULL和任何值比较的结果都是UNKNOWN,会被where子句直接过滤掉。
最终结果相当于内连接:只有那些在dataset1中存在匹配accountid且attribute2='TEST'的account行才会被保留,左表中没有匹配的行全部被筛掉了。
语句2的实际效果(真正的左连接)
select a.accountid, b.attribute1 from account as a left join (select * from dataset1 where attribute2= 'TEST') as b on a.accountid= b.accountid;
这个语句先对右表dataset1做过滤,只保留attribute2='TEST'的行,再和左表做左连接。这时候左连接的逻辑正常生效:account的所有行都会被保留,右表有匹配的就显示对应的attribute1,没有匹配的b.attribute1就是NULL。这才是左连接原本该有的结果。
举个直观例子验证
假设account表有两行:
| accountid |
|---|
| 1 |
| 2 |
dataset1表有两行:
| accountid | attribute1 | attribute2 |
|---|---|---|
| 1 | val1 | TEST |
| 2 | val2 | OTHER |
- 语句1的结果:仅保留
accountid=1的行(accountid=2对应的b.attribute2是OTHER,不满足where条件); - 语句2的结果:两行都保留,
accountid=2的b.attribute1为NULL。
关于左连接后过滤 vs 左连接已过滤右侧的等价性
只有当过滤条件不会排除左连接产生的NULL行时,两者才可能等价,但这种场景非常有限(比如过滤条件是b.attribute2 IS NULL OR b.attribute2='TEST')。
而日常开发中更常见的规律是:
- 如果把右表的过滤条件放在
on子句中(比如left join dataset1 as b on a.accountid=b.accountid and b.attribute2='TEST'),这和语句2完全等价——因为on子句是在连接阶段就过滤右表的行,同时保留左表所有行; - 如果把过滤条件放在
where子句中,只要条件涉及右表的非NULL判断(比如b.attribute2='TEST'),就会把左表无匹配的行过滤掉,直接变成内连接的效果,和先过滤右表再左连接的结果天差地别。
内容的提问来源于stack exchange,提问作者Anthony
相关产品推荐
相关产品推荐

