SQL分组查询的多条件筛选需要根据筛选条件的生效阶段,选择对应的关键字实现,核心是要区分筛选是在分组前生效还是在分组后生效。

核心逻辑区分
分组查询的多条件筛选主要涉及两个关键字:WHERE和HAVING,二者的生效阶段完全不同:
- WHERE:在
GROUP BY分组操作执行之前生效,用于筛选原始数据行,不能使用聚合函数作为筛选条件。 - HAVING:在
GROUP BY分组操作执行之后生效,用于筛选分组后的结果集,可以使用聚合函数作为筛选条件。
单表分组多条件筛选示例
假设存在订单表order_info,表结构如下:
| 字段名 | 类型 | 说明 |
|---|---|---|
| order_id | int | 订单ID |
| user_id | int | 用户ID |
| order_amount | decimal | 订单金额 |
| order_status | int | 订单状态 1待支付 2已支付 3已取消 |
| create_time | datetime | 下单时间 |
需求:统计2024年已支付订单中,单个用户总订单金额超过1000元,且订单数量大于等于3的用户ID和对应总金额。
这个需求包含两类筛选条件:
- 分组前筛选:订单状态为已支付、下单时间在2024年,属于原始行筛选,用
WHERE - 分组后筛选:总订单金额超过1000元、订单数量大于等于3,属于分组结果筛选,用
HAVING
对应的SQL写法如下:
-- 单表分组多条件筛选示例
SELECT
user_id,
COUNT(order_id) AS order_count,
SUM(order_amount) AS total_amount
FROM order_info
-- 分组前筛选条件
WHERE order_status = 2
AND create_time >= '2024-01-01 00:00:00'
AND create_time < '2025-01-01 00:00:00'
-- 分组操作
GROUP BY user_id
-- 分组后筛选条件
HAVING total_amount > 1000
AND order_count >= 3;
多表关联分组多条件筛选示例
假设新增用户表user_info,表结构如下:
| 字段名 | 类型 | 说明 |
|---|---|---|
| user_id | int | 用户ID |
| user_level | int | 用户等级 1普通 2会员 3超级会员 |
需求:统计会员及以上等级用户,2024年已支付订单中,单个用户总订单金额超过1000元,且订单数量大于等于3的用户ID、用户等级和对应总金额。
这个需求新增了用户等级的筛选,属于关联表的原始行筛选,同样放在WHERE中:
-- 多表关联分组多条件筛选示例
SELECT
u.user_id,
u.user_level,
COUNT(o.order_id) AS order_count,
SUM(o.order_amount) AS total_amount
FROM order_info o
-- 关联用户表
INNER JOIN user_info u ON o.user_id = u.user_id
-- 分组前筛选条件,包含关联表的筛选
WHERE o.order_status = 2
AND o.create_time >= '2024-01-01 00:00:00'
AND o.create_time < '2025-01-01 00:00:00'
AND u.user_level >= 2
-- 分组操作
GROUP BY u.user_id, u.user_level
-- 分组后筛选条件
HAVING total_amount > 1000
AND order_count >= 3;
常见错误写法
很多开发者会混淆WHERE和HAVING的使用场景,以下是典型错误示例:
错误1:在WHERE中使用聚合函数
-- 错误写法,WHERE中不能使用聚合函数 SELECT user_id, SUM(order_amount) AS total_amount FROM order_info WHERE SUM(order_amount) > 1000 -- 此处会报错 GROUP BY user_id;
正确写法是将聚合函数筛选放到HAVING中:
-- 正确写法 SELECT user_id, SUM(order_amount) AS total_amount FROM order_info GROUP BY user_id HAVING SUM(order_amount) > 1000;
错误2:在HAVING中写分组前的行筛选条件
虽然部分数据库支持在HAVING中写非聚合的普通条件,但这样会导致先分组再筛选,性能远低于先筛选再分组,不推荐这样写:
-- 不推荐的写法,性能较差 SELECT user_id, SUM(order_amount) AS total_amount FROM order_info GROUP BY user_id HAVING order_status = 2 -- 分组前条件放到HAVING中 AND SUM(order_amount) > 1000;
正确写法是将order_status = 2放到WHERE中。
总结
SQL分组查询多条件筛选的编写核心是先区分筛选条件的生效阶段:原始行筛选、分组前生效的条件用WHERE,分组后、依赖聚合结果的条件用HAVING。多表关联时,关联表的原始行筛选同样放在WHERE中,这样既能保证SQL逻辑正确,也能获得更好的查询性能。