LEFT JOIN条件放置位置不同导致查询结果差异问题咨询
两种SQL写法返回结果差异的根本原因
核心差异来自 LEFT JOIN场景下,ON子句和WHERE子句的过滤逻辑、生效时机完全不同,两种写法的执行流程拆解如下:
第一种写法(右表过滤条件写在WHERE中)的执行逻辑
对应SQL:
SELECT bc.name, SUM(amount) AS total FROM budget_categories bc LEFT JOIN budget_items bi ON bc.id = bi.budget_category_id LEFT JOIN balance_lines bl ON bl.budget_item_id = bi.id WHERE bc.id = 25 AND family_id = 97109 AND bl.balance_id = 264553
执行步骤:
- 先对三张表做左连接:先从
budget_categories取出id=25的分类记录(即name为Bannana的行),再依次左连匹配的budget_items、balance_lines记录,右表匹配不到的字段全部填充为NULL。 - 关联完成后执行WHERE条件全量过滤:此时要求
family_id = 97109、bl.balance_id = 264553。这里的family_id没有指定表别名,从逻辑看属于bl表的字段,如果没有匹配到符合要求的balance_lines记录,bl相关字段全为NULL,而NULL和任何值做等值判断的结果都是不成立,整行会被直接过滤掉。 - 由于这条查询没有写
GROUP BY,聚合函数SUM()即使在空结果集上也会返回一行计算结果,此时左表的bc.name已经随整行被过滤,最终返回NULL | NULL。
这种写法本质上把LEFT JOIN写得和INNER JOIN效果一致:只要右表没有匹配记录,左表的行也会被剔除。
第二种写法(右表过滤条件写在对应LEFT JOIN的ON子句中)的执行逻辑
对应SQL:
SELECT bc.name, SUM(amount) AS total FROM budget_categories bc LEFT JOIN budget_items bi ON bc.id = bi.budget_category_id LEFT JOIN balance_lines bl ON bl.budget_item_id = bi.id AND family_id = 97109 AND bl.balance_id = 264553 WHERE bc.id = 25
执行步骤:
- 先从
budget_categories取出id=25的分类记录(即name为Bannana的行),左连匹配的budget_items记录。 - 关联
balance_lines表时,ON里的三个条件仅用于筛选右表bl中能参与匹配的行:只有同时满足budget_item_id匹配、family_id=97109、balance_id=264553的bl记录才会被关联上来,匹配不到就给bl相关字段填充NULL,这个阶段不会过滤左表已经保留的bc、bi行。 - 关联完成后执行WHERE条件,仅过滤
bc.id=25,左表的Bannana行完全符合要求被保留。此时bl表的amount字段全为NULL,SUM(amount)计算结果为NULL,最终返回预期的Bannana | NULL。
关键规则总结:
- LEFT JOIN中,ON子句的过滤条件仅作用于当前关联的右表,决定右表哪些行能匹配,匹配失败只会给右表字段补NULL,永远不会丢弃左表的合法行
- WHERE子句的过滤条件在所有表关联完成后生效,会对整个临时结果集做行级过滤,只要条件不满足(包括右表字段为NULL导致判断不成立的情况),整行都会被剔除
内容的提问来源于stack exchange,提问作者Noam B.
相关产品推荐
相关产品推荐

