SQL 聚合函数的核心在于与分组、HAVING 筛选、CASE WHEN 条件统计及窗口函数的灵活组合:1. GROUP BY 需包含 SELECT 中所有非聚合字段;2. HAVING 用于筛选分组后结果;3. CASE WHEN 实现单查询多维条件统计;4. 窗口函数支持分组聚合同时保留明细。

SQL 聚合函数本身不难,但真正提升分析效率的,是把它们和分组、过滤、窗口、子查询等机制灵活组合。关键不在“会用 COUNT”,而在“知道什么时候该用 COUNT + CASE WHEN,什么时候该嵌套 AVG(SUM()),什么时候必须加 HAVING 而不是 WHERE”。
用 GROUP BY + 聚合函数做多维分组统计
单字段分组太基础,实际业务常需按多个维度交叉分析。比如统计“每个城市、每种订单状态下的平均订单金额和订单数”:
SELECT city, status, AVG(amount) AS avg_amount, COUNT(*) AS order_cnt
FROM orders
GROUP BY city, status;
注意:SELECT 中所有非聚合字段(city、status)都必须出现在 GROUP BY 子句中;若漏掉某个字段,多数数据库会报错。
用 HAVING 精准筛选分组结果
WHERE 作用于行,HAVING 才作用于分组后的聚合结果。比如只看“订单数超 100 且平均金额高于 500”的城市:
SELECT city, COUNT(*) AS cnt, AVG(amount) AS avg_amt
FROM orders
GROUP BY city
HAVING COUNT(*) > 100 AND AVG(amount) > 500;
常见误区:把聚合条件写在 WHERE 里(如 WHERE AVG(amount) > 500),语法直接报错——因为 WHERE 执行时还没分组,更没算出 AVG。
用 CASE WHEN + 聚合实现条件统计
一个查询里同时算“已支付订单数”、“未支付订单数”、“总金额”、“已支付金额”,不用多次 JOIN 或子查询:
- 用 SUM(CASE WHEN status = ‘paid’ THEN 1 ELSE 0 END) 统计已支付笔数
- 用 SUM(CASE WHEN status = ‘paid’ THEN amount ELSE 0 END) 算已支付金额
- 用 COUNT(*) 和 SUM(amount) 得到全局总数与总额
SELECT
COUNT(*) AS total_orders,
SUM(CASE WHEN status = ‘paid’ THEN 1 ELSE 0 END) AS paid_cnt,
SUM(CASE WHEN status = ‘paid’ THEN amount ELSE 0 END) AS paid_amt,
AVG(amount) AS avg_per_order
FROM orders;
用窗口函数扩展聚合能力
当需要“既看本组聚合值,又保留原始明细”时,窗口函数不可替代。例如:给每个用户的订单按时间排序,并计算该用户订单总金额、当前订单占该用户总额的比例:
SELECT
user_id, order_time, amount,
SUM(amount) OVER (PARTITION BY user_id) AS user_total,
ROUND(amount * 100.0 / SUM(amount) OVER (PARTITION BY user_id), 2) AS pct_of_user
FROM orders;
注意 PARTITION BY 相当于“按 user_id 分组做聚合,但不折叠行”,比 GROUP BY 更灵活;ORDER BY 可选,但加了就能支持累计求和(如 SUM(amount) OVER (PARTITION BY user_id ORDER BY order_time))。