SQL 条件聚合:超越基础 GROUP BY

你已经掌握了 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。


相关文章推荐


本文转载自 www.hisqlboy.com

原文标题:SQL Conditional Aggregation: Beyond Basic GROUP BY

原文链接:https://www.hisqlboy.com/blog/conditional-aggregation-sql

原作者:SQL Boy Team

转载日期:2026-02-08

著作权归原作者所有。本文仅用于学习交流,非商业用途。

相关推荐
随风飘的云5 小时前
MySQL的慢查询优化解决思路
数据库
IvorySQL9 小时前
PostgreSQL 技术日报 (3月7日)|生态更新与内核性能讨论
数据库·postgresql·开源
赵渝强老师10 小时前
【赵渝强老师】金仓数据库的数据文件
数据库·国产数据库·kingbase·金仓数据库
随逸17713 小时前
《Milvus向量数据库从入门到实战,手把手搭建语义检索系统》
数据库
神秘的猪头14 小时前
🚀 React 开发者进阶:RAG 核心——手把手带你玩转 Milvus 向量数据库
数据库·后端·llm
IvorySQL1 天前
PostgreSQL 技术日报 (3月6日)|为什么 Ctrl-C 在 psql 里让人不安?
数据库·postgresql·开源
NineData1 天前
数据库管理工具NineData,一年进化成为数万+开发者的首选数据库工具?
运维·数据结构·数据库
IvorySQL2 天前
PostgreSQL 技术日报 (3月5日)|规划器控制力升级,内核能力再进阶
数据库·postgresql·开源
数据组小组2 天前
免费数据库管理工具深度横评:NineData 社区版、Bytebase 社区版、Archery,2026 年开发者该选哪个?
数据库·测试·数据库管理工具·数据复制·迁移工具·ninedata社区版·naivicat平替