使用CTE与CASE语句实现行压缩的技术问题
解决SQL查询结果分组压缩(行转列)的问题
嘿,我完全懂你现在的困扰——就是想把同一个人的多个Plan ID合并到一行里,而不是拆成每行只有一个Plan ID的多行数据对吧?我给你分享两种实用的解决办法,应该能完美匹配你的需求。
方法一:条件聚合(通用所有SQL数据库)
这种方法兼容性极强,几乎所有支持SQL的数据库都能使用,核心思路是给每个用户的Plan ID分配序号,再通过条件判断+聚合函数把多行合并成一行。
修改你的CTE,加入行号生成逻辑,再用CASE WHEN结合MAX聚合:
;WITH CTE AS ( SELECT p.id [Person ID], plan.id [Plan ID], -- 给每个Person的Plan ID按顺序编号(1、2、3...),排序规则可按需调整 ROW_NUMBER() OVER (PARTITION BY p.id ORDER BY plan.id) AS PlanSeq FROM -- 这里替换成你原本的表连接逻辑,比如 person p JOIN plan_table plan ON ... ) SELECT [Person ID], MAX(CASE WHEN PlanSeq = 1 THEN [Plan ID] END) AS [Plan ID 1], MAX(CASE WHEN PlanSeq = 2 THEN [Plan ID] END) AS [Plan ID 2], MAX(CASE WHEN PlanSeq = 3 THEN [Plan ID] END) AS [Plan ID 3] FROM CTE GROUP BY [Person ID];
关键逻辑说明:
ROW_NUMBER() OVER (PARTITION BY p.id ORDER BY plan.id):按Person ID分组,给每个组内的Plan ID分配唯一序号,ORDER BY可以根据实际需求调整(比如按Plan创建时间排序)。MAX(CASE ...):针对每个序号筛选对应的Plan ID,用MAX聚合是因为同一Person+序号组合只有一个非NULL值,最终会把多行的非NULL值合并到一行的对应列中。
方法二:使用PIVOT(适用于SQL Server、Oracle 11g+等支持PIVOT的数据库)
如果你的数据库支持PIVOT语法,这种写法会更简洁,同样需要先给Plan ID生成序号,再通过PIVOT转成列:
;WITH CTE AS ( SELECT p.id [Person ID], plan.id [Plan ID], -- 生成对应的列名,比如 "Plan ID 1"、"Plan ID 2" 'Plan ID ' + CAST(ROW_NUMBER() OVER (PARTITION BY p.id ORDER BY plan.id) AS VARCHAR) AS PlanColumn FROM -- 替换成你原本的表连接逻辑 ) SELECT [Person ID], [Plan ID 1], [Plan ID 2], [Plan ID 3] FROM CTE PIVOT ( MAX([Plan ID]) -- 聚合函数,这里用MAX/MIN都可以 FOR PlanColumn IN ([Plan ID 1], [Plan ID 2], [Plan ID 3]) -- 指定要转成的列 ) AS PivotResult;
注意事项:
- 如果用户的Plan数量可能超过3个,只需要在SELECT和PIVOT的IN子句中添加对应的列(比如
[Plan ID 4])即可。 - 若某个Person没有对应序号的Plan,对应的列会自动显示为
NULL,完全符合你期望的结果格式。
内容的提问来源于stack exchange,提问作者user3007447
相关产品推荐
相关产品推荐

