Redshift中LEFT JOIN两种条件写法的执行差异解析及疑问:WHERE子句后置vs ON子句附加条件
咱们直接把这两种写法的核心差异和你的理解误区掰明白:
一、两种写法的逻辑本质完全不同
1. 写法一:WHERE子句过滤主表(table1)
explain select * from table1 t left join table2 t2 on t.key = t2.key where t.snapshot_day = to_date('2021-12-18', 'YYYY-MM-DD');
逻辑行为:
先从table1里筛出所有snapshot_day = '2021-12-18'的行,再把这些筛选后的行和table2做LEFT JOIN——也就是说,最终结果里只会保留table1中符合日期条件的行,匹配不到table2的行则table2字段为NULL。
执行计划验证:
你看执行计划里table1的Seq Scan带了Filter,只返回了287万多行,之后做哈希的成本极低,最终返回行数和主表筛选后的行数基本一致,这就是优化器做了谓词下推——把WHERE里的主表过滤提前到JOIN之前执行,减少了后续JOIN的数据量。
2. 写法二:ON子句附加主表条件
explain select * from table1 t left join table2 t2 on t.key= t2.key and t.snapshot_day = to_date('2021-12-18', 'YYYY-MM-DD');
逻辑行为:
保留table1的所有行,然后对table1的每一行,只有当该行的snapshot_day = '2021-12-18'时,才会去匹配table2中key相等的行;如果table1的行日期不符合,哪怕key能匹配table2,table2的字段也会是NULL。简单说,这个条件是用来限制右表匹配的规则,而不是过滤左表。
执行计划验证:
执行计划里table1的Seq Scan没有Filter,扫描了全部32亿多行,哈希表的成本直接拉满,最终返回行数和table1总行数一致——这就是因为左表没有被过滤,所有行都保留了。
二、你的理解误区在哪里?
误区一:WHERE子句一定在JOIN之后执行
其实对于LEFT JOIN的左表,优化器会自动判断:如果WHERE里的条件只涉及左表,那么可以安全地把过滤逻辑提前到JOIN之前(谓词下推),这不会改变查询结果,还能大幅减少数据处理量。你的执行计划已经证明了这一点。误区二:ON子句中的主表条件会过滤主表
LEFT JOIN的核心是保留左表所有行,ON子句的条件只是用来决定右表哪些行能和左表行匹配,完全不会过滤左表本身。只有INNER JOIN的ON条件才会同时过滤左右表,LEFT JOIN的ON条件对左表没有过滤作用。
三、总结建议
- 如果需要过滤左表的行(只保留符合条件的左表行再关联),一定要用WHERE子句写左表的过滤条件;
- 如果是想保留左表所有行,但仅当左表行符合某条件时才去匹配右表,才用ON子句附加左表条件——这种场景其实很少见,大部分时候你需要的是第一种写法。
内容的提问来源于stack exchange,提问作者Heisenberg

