你已经掌握了 COUNT()、SUM() 和 GROUP BY。但是,如果你需要只统计某些特定的行,或者在单个查询中创建透视表呢?
条件聚合(Conditional Aggregation) 将 CASE WHEN 与聚合函数结合,无需多个查询或子查询即可回答复杂问题。

图示说明:条件聚合可以将原始数据表转换为多个关键绩效指标(KPI)卡片
基本模式
传统方式需要两个独立的查询:
sql
-- 两个独立的查询
SELECT COUNT(*) FROM orders WHERE status = 'completed';
SELECT COUNT(*) FROM orders WHERE status = 'pending';
使用条件聚合,只需一个查询:
sql
SELECT
COUNT(CASE WHEN status = 'completed' THEN 1 END) as completed,
COUNT(CASE WHEN status = 'pending' THEN 1 END) as pending
FROM orders;
一个查询,一行结果,所有答案!
交互式示例 1:订单状态报告
注意:原文中这是一个可交互的 SQL 编辑器,读者可以修改并运行代码。以下是完整的 SQL 代码示例:
sql
-- 在单行中按状态统计订单
SELECT
COUNT(*) as total_orders,
COUNT(CASE WHEN status = 'completed' THEN 1 END) as completed,
COUNT(CASE WHEN status = 'pending' THEN 1 END) as pending,
COUNT(CASE WHEN status = 'cancelled' THEN 1 END) as cancelled
FROM orders;
示例数据(orders 表):
| order_id | status | amount |
|---|---|---|
| 1 | completed | 150.00 |
| 2 | pending | 200.00 |
| 3 | completed | 300.00 |
| 4 | cancelled | 100.00 |
| 5 | completed | 250.00 |
查询结果:
| total_orders | completed | pending | cancelled |
|---|---|---|---|
| 5 | 3 | 1 | 1 |
带条件的 SUM
按状态计算收入:
注意:原文中这是一个可交互的 SQL 编辑器。以下是完整的 SQL 代码示例:
sql
-- 按状态汇总金额
SELECT
SUM(amount) as total_revenue,
SUM(CASE WHEN status = 'completed' THEN amount ELSE 0 END) as completed_revenue,
SUM(CASE WHEN status = 'pending' THEN amount ELSE 0 END) as pending_revenue
FROM orders;
查询结果:
| total_revenue | completed_revenue | pending_revenue |
|---|---|---|
| 1000.00 | 700.00 | 200.00 |
注意 :在 SUM() 中使用 ELSE 0 以避免 NULL 问题。如果不使用 ELSE 0,不满足条件的行会返回 NULL,这会导致求和结果不正确。
创建透视表
将行转换为列 - 经典的透视表操作:
注意:原文中这是一个可交互的 SQL 编辑器。以下是完整的 SQL 代码示例:
sql
-- 透视:按月份列显示销售额
SELECT
product,
SUM(CASE WHEN month = 'Jan' THEN sales ELSE 0 END) as jan_sales,
SUM(CASE WHEN month = 'Feb' THEN sales ELSE 0 END) as feb_sales,
SUM(CASE WHEN month = 'Mar' THEN sales ELSE 0 END) as mar_sales
FROM monthly_sales
GROUP BY product;
示例数据(monthly_sales 表):
| product | month | sales |
|---|---|---|
| Laptop | Jan | 5000 |
| Laptop | Feb | 6000 |
| Laptop | Mar | 5500 |
| Phone | Jan | 3000 |
| Phone | Feb | 3500 |
| Phone | Mar | 4000 |
查询结果:
| product | jan_sales | feb_sales | mar_sales |
|---|---|---|---|
| Laptop | 5000 | 6000 | 5500 |
| Phone | 3000 | 3500 | 4000 |
这将垂直的月份数据转换为水平列 - 非常适合报告!
计算百分比
将条件计数与总计数结合:
注意:原文中这是一个可交互的 SQL 编辑器。以下是完整的 SQL 代码示例:
sql
-- 计算完成率百分比
SELECT
COUNT(*) as total,
COUNT(CASE WHEN status = 'completed' THEN 1 END) as completed,
ROUND(COUNT(CASE WHEN status = 'completed' THEN 1 END) * 100.0 / COUNT(*), 2) as completion_rate
FROM orders;
查询结果:
| total | completed | completion_rate |
|---|---|---|
| 5 | 3 | 60.00 |
关键点 :使用 * 100.0(而不是 * 100)确保结果是浮点数,从而得到精确的百分比。
条件 AVG 和 MIN/MAX
条件聚合适用于任何聚合函数:
注意:原文中这是一个可交互的 SQL 编辑器。以下是完整的 SQL 代码示例:
sql
-- 按类别比较平均值
SELECT
AVG(price) as overall_avg,
AVG(CASE WHEN category = 'Electronics' THEN price END) as electronics_avg,
AVG(CASE WHEN category = 'Books' THEN price END) as books_avg
FROM products;
示例数据(products 表):
| product_id | category | price |
|---|---|---|
| 1 | Electronics | 500.00 |
| 2 | Electronics | 800.00 |
| 3 | Books | 20.00 |
| 4 | Books | 30.00 |
查询结果:
| overall_avg | electronics_avg | books_avg |
|---|---|---|
| 337.50 | 650.00 | 25.00 |
与 GROUP BY 结合
为每个分组创建详细的细分:
注意:原文中这是一个可交互的 SQL 编辑器。以下是完整的 SQL 代码示例:
sql
-- 每个客户的状态细分
SELECT
customer_id,
COUNT(*) as total_orders,
COUNT(CASE WHEN status = 'completed' THEN 1 END) as completed,
COUNT(CASE WHEN status = 'pending' THEN 1 END) as pending
FROM orders
GROUP BY customer_id;
示例数据(orders 表):
| order_id | customer_id | status |
|---|---|---|
| 1 | 101 | completed |
| 2 | 101 | pending |
| 3 | 101 | completed |
| 4 | 102 | completed |
| 5 | 102 | completed |
查询结果:
| customer_id | total_orders | completed | pending |
|---|---|---|---|
| 101 | 3 | 2 | 1 |
| 102 | 2 | 2 | 0 |
使用 CASE 的布尔标志
先为每行创建标志,然后聚合:
sql
-- 标记订单,然后汇总
SELECT
SUM(is_high_value) as high_value_count,
SUM(is_new_customer) as new_customer_orders
FROM (
SELECT
*,
CASE WHEN amount > 500 THEN 1 ELSE 0 END as is_high_value,
CASE WHEN customer_age_days < 30 THEN 1 ELSE 0 END as is_new_customer
FROM orders
);
这种方法在需要多次使用相同条件时特别有用,可以提高代码的可读性。
快速参考
| 目标 | 模式 |
|---|---|
| 条件计数 | COUNT(CASE WHEN cond THEN 1 END) |
| 条件求和 | SUM(CASE WHEN cond THEN value ELSE 0 END) |
| 条件平均 | AVG(CASE WHEN cond THEN value END) |
| 百分比 | COUNT(CASE...) * 100.0 / COUNT(*) |
| 透视表 | 每列使用 SUM(CASE WHEN col = 'X' ...) |
常见错误
1. 在 SUM 中忘记 ELSE
错误:
sql
SUM(CASE WHEN status = 'completed' THEN amount END)
问题 :没有 ELSE 0,你会得到 NULL,这会导致求和结果不正确。
正确:
sql
SUM(CASE WHEN status = 'completed' THEN amount ELSE 0 END)
2. 在 COUNT 中使用 ELSE
不需要:
sql
COUNT(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) -- 错误!
原因 :COUNT(NULL) 自动返回 0,不需要 ELSE。
正确:
sql
COUNT(CASE WHEN status = 'completed' THEN 1 END)
3. 复杂条件
在 CASE 内部使用 AND/OR:
sql
COUNT(CASE WHEN status = 'completed' AND amount > 100 THEN 1 END)
PostgreSQL FILTER 子句
PostgreSQL 提供了更清晰的语法:
sql
-- 仅限 PostgreSQL
SELECT
COUNT(*) FILTER (WHERE status = 'completed') as completed,
SUM(amount) FILTER (WHERE status = 'pending') as pending_total
FROM orders;
这与 CASE WHEN 方法等效,但更易读。如果你使用 PostgreSQL,建议优先使用 FILTER 子句。
结论
条件聚合是一种报告构建的超能力:
简化查询:将多个查询合并为一个,减少数据库往返次数,提高性能。
创建透视表:无需复杂的连接即可创建透视表,使数据呈现更加直观。
计算百分比和比率:轻松计算各种业务指标,如完成率、转化率等。
构建仪表板:使用单个 SQL 语句构建完整的仪表板,所有 KPI 一目了然。
模式很简单:将 CASE WHEN 放在任何聚合函数内部。掌握这项技术,你将为任何分析报告任务编写更清晰、更快速的 SQL。
相关文章推荐
-
在 SQL 中计算加权平均值:标准平均值可能会产生误导。了解如何在 SQL 中计算加权平均值,以从数据中获得更准确的洞察。
-
精通 SQL 中的 ROLLUP、CUBE 和 GROUPING SETS:停止为小计运行多个查询。了解如何使用高级 GROUP BY 扩展在一次传递中生成强大的报告。
-
使用 SQL 进行时间序列分析:趋势、增长和移动平均:将原始时间戳转化为业务洞察。了解如何计算环比增长并使用 7 天移动平均平滑嘈杂的数据。
本文转载自 www.hisqlboy.com
原文标题:SQL Conditional Aggregation: Beyond Basic GROUP BY
原文链接:https://www.hisqlboy.com/blog/conditional-aggregation-sql
原作者:SQL Boy Team
转载日期:2026-02-08