SQL易混点:你知道ON 和 WHERE 的区别吗
目录
文章目标
🧩 背景回顾:SQL 的执行顺序
示例对比
✅ ON 应该只管:这两行能不能配对
✅ 正确做法:把过滤放在 WHERE
🔬 实验演示:ON vs. WHERE 的影响
总结:
🏁 最终推荐 SQL 写法
文章目标
我们要解决的问题是:
在 JOIN 查询中,哪些条件写在 ON
后面,哪些写在 WHERE
后面?为什么?
这个问题,初看像语法细节,实则深藏两个重要概念:
-
SQL 执行顺序(JOIN → WHERE)
-
条件的语义(配对 vs. 过滤)
🧩 背景回顾:SQL 的执行顺序
在一条 SQL 中,执行步骤大致如下:
-
FROM
和JOIN
:把多个表合并,按ON
的规则配对行 -
WHERE
:对配对成功的行进行行级过滤 -
GROUP BY
:对通过筛选的行进行分组 -
SELECT
:选出你需要的字段 -
ORDER BY
/LIMIT
等
所以:
ON
作用在连接阶段,是“决定哪些行要配成一对”
WHERE
是后续过滤,是“已经 JOIN 上了,选谁要留下”
示例对比
我们仍以Leecode上的一道题来分析。这道题的思路讲解可以看我之前的文章:
思路解析:第一性原理解 SQL-CSDN博客
表: Activity
+----------------+---------+
| Column Name | Type |
+----------------+---------+
| machine_id | int |
| process_id | int |
| activity_type | enum |
| timestamp | float |
+----------------+---------+
该表展示了一家工厂网站的用户活动。
(machine_id, process_id, activity_type) 是当前表的主键(具有唯一值的列的组合)。
machine_id 是一台机器的ID号。
process_id 是运行在各机器上的进程ID号。
activity_type 是枚举类型 ('start', 'end')。
timestamp 是浮点类型,代表当前时间(以秒为单位)。
'start' 代表该进程在这台机器上的开始运行时间戳 , 'end' 代表该进程在这台机器上的终止运行时间戳。
同一台机器,同一个进程都有一对开始时间戳和结束时间戳,而且开始时间戳永远在结束时间戳前面。
现在有一个工厂网站由几台机器运行,每台机器上运行着 相同数量的进程 。编写解决方案,计算每台机器各自完成一个进程任务的平均耗时。
完成一个进程任务的时间指进程的'end' 时间戳
减去 'start' 时间戳
。平均耗时通过计算每台机器上所有进程任务的总耗费时间除以机器上的总进程数量获得。
结果表必须包含machine_id(机器ID)
和对应的 average time(平均耗时) 别名 processing_time
,且四舍五入保留3位小数。
以 任意顺序 返回表。(来源:Leecode)
SELECT machine_id, ROUND(AVG(t2.timestamp - t1.timestamp), 3) AS processing_time
FROM Activity t1
JOIN Activity t2ON t1.machine_id = t2.machine_idAND t1.process_id = t2.process_idAND t1.activity_type = 'start'AND t2.activity_type = 'end'
GROUP BY machine_id;
这段代码可以运行,但不严谨。原因如下:
✅ ON
应该只管:这两行能不能配对
这些条件是合理的 ON
条件:
ON t1.machine_id = t2.machine_id
AND t1.process_id = t2.process_id
解释:我们只配同一机器、同一进程的事件,没错。
❌ 不推荐在 ON
中写:
AND t1.activity_type = 'start'
AND t2.activity_type = 'end'
为什么?
-
你想过滤的是:t1 是 start,t2 是 end
-
这是你的业务逻辑,不是连接逻辑
-
放在
ON
会影响 JOIN 结果集(可能 JOIN 失败),而非只是筛选
✅ 正确做法:把过滤放在 WHERE
FROM Activity t1
JOIN Activity t2ON t1.machine_id = t2.machine_idAND t1.process_id = t2.process_id
WHERE t1.activity_type = 'start'AND t2.activity_type = 'end'
这样写的意义是:
-
先配对所有同机同进程的记录
-
然后从中筛出我们感兴趣的组合(
start
和end
)
🔬 实验演示:ON vs. WHERE 的影响
假设 Activity
表中有:
machine_id | process_id | activity_type | timestamp |
---|---|---|---|
1 | 101 | start | 10.0 |
1 | 101 | end | 20.0 |
1 | 101 | error | 15.0 |
如果你把 'start'
/ 'end'
写在 ON
中:
-
JOIN
阶段只会配出(start, end)
-
那些
error
就永远不会参与配对
如果你写在 WHERE
中:
-
JOIN
先配出所有 (start, end)、(start, error)、(end, error) 等 -
然后你可以灵活过滤你关心的组合
这让 SQL 更清晰、也更灵活
总结:
-
✳️
ON
是 “谁和谁能配成一对?”(连接规则) -
✳️
WHERE
是 “配好之后,我要不要这行?”(业务逻辑)
🏁 最终推荐 SQL 写法
SELECT machine_id, ROUND(AVG(t2.timestamp - t1.timestamp), 3) AS processing_time
FROM Activity t1
JOIN Activity t2ON t1.machine_id = t2.machine_idAND t1.process_id = t2.process_id
WHERE t1.activity_type = 'start'AND t2.activity_type = 'end'
GROUP BY machine_id;