如何高效用SQL按程序分组统计各状态行数并转列为列?
问题描述
我有一个程序表,表结构及数据如下:
| ID | 程序(Program) | 经办人(AssignedPerson) | 状态(Status) |
|---|---|---|---|
| 1 | Sample Program A | Person A | 已完成(Done) |
| 2 | Sample Program A | Person B | 已取消(Cancelled) |
| 3 | Sample Program A | Person C | 未启动(Not Started) |
| 4 | Sample Program B | Person A | 已完成(Done) |
| 5 | Sample Program B | Person C | 未启动(Not Started) |
| 6 | Sample Program C | Person B | 已完成(Done) |
需要对该表进行汇总,生成按唯一程序分组的各状态行数统计结果,目标表结构及数据如下:
| 程序(Program) | 已完成(Done) | 已取消(Cancelled) | 未启动(NotStarted) |
|---|---|---|---|
| Sample Program A | 1 | 1 | 1 |
| Sample Program B | 1 | 1 | |
| Sample Program C | 1 |
**实现该需求的最高效方式是什么?**我担心使用子查询会导致查询缓慢,想了解是否存在更优的实现方案?
最优实现方案
最高效的方式是使用条件聚合(CASE WHEN + GROUP BY),这种方式仅需扫描一次原始表,避免了子查询多次扫描表的性能损耗,是这类行转列统计场景的最优解。
示例SQL代码(以MySQL为例)
SELECT `程序(Program)`, SUM(CASE WHEN `状态(Status)` = '已完成(Done)' THEN 1 ELSE 0 END) AS `已完成(Done)`, SUM(CASE WHEN `状态(Status)` = '已取消(Cancelled)' THEN 1 ELSE 0 END) AS `已取消(Cancelled)`, SUM(CASE WHEN `状态(Status)` = '未启动(Not Started)' THEN 1 ELSE 0 END) AS `未启动(NotStarted)` FROM 程序表 GROUP BY `程序(Program)`;
性能优势说明
- 子查询方案通常需要为每个状态单独查询一次表(比如3个子查询就扫3次表),而条件聚合只需要一次全表扫描,数据量越大性能差距越明显。
- 可以通过在
程序(Program)和状态(Status)字段上建立联合索引(INDEX idx_program_status (程序(Program),状态(Status))),进一步优化查询速度,让数据库直接通过索引完成分组和统计,无需回表。
其他数据库等价实现
如果使用支持PIVOT语法的数据库(如SQL Server、Oracle),也可以用PIVOT函数,本质和条件聚合逻辑一致,性能相当:
-- SQL Server 示例 SELECT [程序(Program)], [已完成(Done)], [已取消(Cancelled)], [未启动(Not Started)] AS [未启动(NotStarted)] FROM 程序表 PIVOT ( COUNT(ID) FOR [状态(Status)] IN ([已完成(Done)], [已取消(Cancelled)], [未启动(Not Started)]) ) AS PivotTable;
内容的提问来源于stack exchange,提问作者user3035024
相关产品推荐
相关产品推荐

