SQL 连接边界实验室:ON、WHERE 与 HAVING 的三层过滤

举报
yd_239005675 发表于 2026/10/22 08:14:07 2026/10/22
【摘要】 用实验对照 ON、WHERE 与 HAVING,理解 LEFT JOIN 补齐行、三值逻辑和 COUNT(o.id) 的边界。

SQL 查询里,条件放在哪里会改变结果的形状。把 status = ‘paid’ 放进 LEFT JOIN … ON,可以保留没有已支付订单的用户;把它放进 WHERE,这些用户可能整行消失;当结果按用户分组时,HAVING 又会在分组之后过滤用户组。

一组固定数据

四个用户里,小林有一笔已支付和一笔未支付订单,小周只有未支付订单,小陈没有订单,小吴的订单状态是 NULL。另有一笔孤立订单,userId 不属于左表。

CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL);
CREATE TABLE orders (id INTEGER PRIMARY KEY, userId INTEGER, status TEXT);
INSERT INTO users VALUES (1, '小林'), (2, '小周'), (3, '小陈'), (4, '小吴');
INSERT INTO orders VALUES (101, 1, 'paid'), (102, 1, 'unpaid'), (103, 2, 'unpaid'), (104, 4, NULL), (105, 99, 'paid');

条件放进 ON:左表仍然完整

SELECT u.id, u.name, o.id AS orderId, o.status
FROM users u
LEFT JOIN orders o ON u.id = o.userId AND o.status = 'paid'
ORDER BY u.id, o.id;

连接阶段只把已支付订单视为匹配。小林得到订单 101;小周、小陈和小吴没有符合条件的订单,各自得到一行由 LEFT JOIN 补出的 NULL。这正好表达“列出所有用户,并附带已支付订单”。

条件放进 WHERE:补齐行也会被过滤

SELECT u.id, u.name, o.id AS orderId, o.status
FROM users u
LEFT JOIN orders o ON u.id = o.userId
WHERE o.status = 'paid'
ORDER BY u.id, o.id;

这里先连接全部订单,再过滤 status。小周的真实订单是 unpaid,小吴的状态是 NULL,小陈的补齐行也是 NULL。对于 NULL = ‘paid’,结果是 UNKNOWN;WHERE 只保留 TRUE,所以这三个人都会消失。

如果同时要求订单编号为空,只能救回完全没有订单的小陈。小周和小吴有真实订单,订单编号非空,仍然无法通过。

分组之后再用 HAVING

如果需求变成“只保留至少有一笔已支付订单的用户组”,可以把支付条件放进 ON,再按用户分组:

SELECT u.id, u.name, COUNT(o.id) AS paidCount
FROM users u
LEFT JOIN orders o ON u.id = o.userId AND o.status = 'paid'
GROUP BY u.id, u.name
HAVING COUNT(o.id) > 0;

WHERE 过滤单行,HAVING 过滤分组。连接后的补齐行仍然存在,但补齐行的订单编号为空,不会被 COUNT 计数;因此只有小林这一组通过 HAVING。如果使用 COUNT(*),补齐行也会被计入,语义就变成每个用户组至少有一行结果。

用实验观察三值逻辑

配套的 SQL 连接边界实验室把五种写法并排展示,并增加聚合后的支付订单数大于 0 结果。可以切换一对多订单、空右表和全 NULL 状态,观察真实订单、补齐行、UNKNOWN 与组级过滤的区别。

实验页面只使用固定内存数据,不连接生产数据库、不上传输入,也不执行任意 SQL。它是语义教学工具;真实系统仍应结合约束、数据质量、索引和执行计划验证方案。

参考 PostgreSQL 的表表达式和 SQLite 的 SELECT 文档。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。