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

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

相关推荐
hacklf20082 小时前
数据库高安全—openGauss安全整体架构&安全认证
数据库·安全·架构
全栈前端老曹2 小时前
【Redis】 监控与慢查询日志 —— slowlog、INFO 命令、RedisInsight 可视化监控
前端·数据库·redis·缓存·全栈·数据库监控·slowlog
知识分享小能手2 小时前
SQL Server 2019入门学习教程,从入门到精通,SQL Server 2019 规则、默认值和完整性约束 —— 语法详解与实战案例(11)
sql·学习·sqlserver
木子欢儿2 小时前
debian 13 安装配置ftp 创建用户admin可以访问 /mnt/Data/
linux·运维·服务器·数据库·debian
LSL666_2 小时前
3 Redis 的 Java 客户端
java·数据库·redis
长而不宰2 小时前
一个偏爱确定性的人如何面对家庭压力
sql
Moshow郑锴2 小时前
PostgreSQL备份机制详解(WAL+Logical Dump+Physical Backup)
数据库·postgresql
linxinglu2 小时前
Oracle:关系型数据库的锁定与数据引力场
数据库·oracle
last_zhiyin2 小时前
Oracle sql tuning guide 翻译 Part 4-2 --- 连接方法(Join Methods)
数据库·sql