1. SELECT 子句中的过滤
作用
SELECT 子句本身并不用于直接过滤数据行 ,而是用于表达式计算和返回结果列。
但有两种特殊"伪过滤"情况,初学者容易混淆:
a) 使用CASE WHEN生成标志位
sql
SELECT
id,
amount,
CASE WHEN amount > 500 THEN '大额' ELSE '小额' END AS grade
FROM orders;
这里只是标记,并没有"过滤"数据行。
b) 利用窗口函数的 FILTER
sql
SELECT
SUM(amount) FILTER (WHERE type = 'A') AS sum_a,
SUM(amount) FILTER (WHERE type = 'B') AS sum_b
FROM orders;
这里的 FILTER 是对聚合函数的聚合过滤,不影响整行是否被选中,只决定聚合时哪些被算上。
总结
SELECT 不能直接过滤结果行 (除了各种聚合"过滤"标志)。真正过滤结果应放在 WHERE 或 JOIN 或 HAVING。
2. JOIN 子句中的过滤(ON 条件)
JOIN 的 ON 部分与 WHERE 条件不同:
a) INNER JOIN
sql
SELECT *
FROM a
JOIN b ON a.id = b.a_id AND b.status = 'active'
解析
b.status = 'active'写在ON内,只有满足 a.id=b.a_id 且 b.status='active' 时,才产生一对结果。- 即,在关联时,提前筛选对方表的相关数据。
b) LEFT JOIN
sql
SELECT *
FROM a
LEFT JOIN b ON a.id = b.a_id AND b.status = 'active'
解析
LEFT JOIN不管 b 有无匹配,总会保留 a。ON过滤是:如果 b.status 不为 'active',则 b 列为 NULL;不会把整行 a 去掉。
c) 过滤条件写在 WHERE
sql
SELECT *
FROM a
LEFT JOIN b ON a.id = b.a_id
WHERE b.status = 'active'
解析
- 此写法:
WHERE条件会把 a 的那些 b 没有匹配到的(所有 b 为 NULL 的)全部去掉,实际上结果和 INNER JOIN 效果一样!
3. WHERE 子句中的过滤
WHERE 负责数据源(包括 join 后的中间结果)的最终行过滤。
sql
SELECT *
FROM a
JOIN b ON a.id = b.a_id
WHERE a.amount > 100 AND b.status = 'active'
- 先连接,再整体过滤。
4. 典型案例对比
假如有如下两个表:
- students(id, name)
- scores(student_id, subject, mark)
假设每个学生有多个成绩。
场景1:只看及格成绩
条件写在JOIN:
sql
SELECT s.id, s.name, sc.mark
FROM students s
JOIN scores sc ON s.id = sc.student_id AND sc.mark >= 60
🚩只有及格的匹配行会被连接,学生不会重复出现不及格的成绩。
条件写在WHERE:
sql
SELECT s.id, s.name, sc.mark
FROM students s
JOIN scores sc ON s.id = sc.student_id
WHERE sc.mark >= 60
🚩结果一样:只显示及格成绩。
LEFT JOIN + 过滤条件写在JOIN:
sql
SELECT s.id, s.name, sc.mark
FROM students s
LEFT JOIN scores sc ON s.id = sc.student_id AND sc.mark >= 60
🚩每个学生会显示所有自己及格的成绩,没及格就为 NULL,但学生都在。
LEFT JOIN + 过滤条件写在WHERE:
sql
SELECT s.id, s.name, sc.mark
FROM students s
LEFT JOIN scores sc ON s.id = sc.student_id
WHERE sc.mark >= 60
🚩所有没及格的行(包括没考试的、sc.mark是NULL),全部被过滤掉,效果和INNER JOIN一样!
注意:LEFT JOIN+WHERE条件,在想保留主表(比如students)所有数据但只展示部分关联的情况下,要特别小心写法。
5. 总结表格
| 过滤条件位置 | 作用对象 | 推荐场景 |
|---|---|---|
| SELECT | 只对输出列做条件标记、赋值 | 需展示条件分组/标识 |
| JOIN (ON) | 只影响表之间的连接配对关系 | 只过滤被联接表时,主表全保留 |
| WHERE | 影响整个结果最终输出行 | 需要严格筛选全部输出的情况 |
一句话总结
- 只保留主表相关数据 :
LEFT JOIN ... ON ... - 真正只显示符合条件整行 :
WHERE - 用于联表附加限定,不影响主表全保留 :
JOIN ON - SELECT只做数据衍生、打标,不过滤行