SQL Server 2017 日历应用场景下T-SQL行转列PIVOT实现方案
问题根因
你之前的PIVOT写法无法得到多行结果的核心原因是未将同周同天的序号rn纳入分组维度:PIVOT运算符会默认将源数据中「除聚合列、转置列之外的所有字段」作为分组键,你之前的子查询仅返回wk/subj/day三个字段,分组键只有wk,自然每个周只会聚合出1条结果。
最优实现方案(适配SQL Server 2017)
采用「生成周行骨架+条件聚合」的实现方式,逻辑显式无隐式规则,性能与原生PIVOT一致,还能自动补全空值位置的缺失行,代码如下:
-- 统计每个周需要展示的最大行数(由该周日程最多的那天决定) WITH WeekMaxRn AS ( SELECT wk, MAX(rn) AS max_rn FROM mytable GROUP BY wk ), -- 递归生成每个周下从1到max_rn的连续行序号,作为多日日程对齐的骨架 WeekRowSkeleton AS ( SELECT wk, 1 AS rn FROM WeekMaxRn UNION ALL SELECT w.wk, w.rn + 1 FROM WeekRowSkeleton w JOIN WeekMaxRn m ON w.wk = m.wk AND w.rn < m.max_rn ) -- 左关联原日程表,按星期做条件聚合得到日历结构 SELECT s.wk, MAX(CASE WHEN t.day = 'mon' THEN t.subj END) AS mon, MAX(CASE WHEN t.day = 'tue' THEN t.subj END) AS tue, MAX(CASE WHEN t.day = 'wed' THEN t.subj END) AS wed -- 需支持周四周五周六周日时,在此处补充对应CASE判断即可 FROM WeekRowSkeleton s LEFT JOIN mytable t ON s.wk = t.wk AND s.rn = t.rn GROUP BY s.wk, s.rn ORDER BY s.wk, s.rn;
写法说明
- 生成行骨架的步骤是必要的:如果某一天的日程数量比同周其他天多,骨架会自动补齐其他天对应位置的空行,不会出现最后一条日程漏展示的问题。
- 条件聚合相比原生PIVOT可读性更高:没有隐式分组的黑盒逻辑,扩展星期列仅需新增一行CASE判断,不需要修改多处语法位置。
- 执行上述代码可以完全得到你给出的预期结果,空值位置会自动填充为NULL。
如果你坚持使用PIVOT语法,只需要在源数据子查询中保留rn字段,同时基于上述骨架关联数据即可,示例代码如下:
WITH WeekMaxRn AS ( SELECT wk, MAX(rn) AS max_rn FROM mytable GROUP BY wk ), WeekRowSkeleton AS ( SELECT wk, 1 AS rn FROM WeekMaxRn UNION ALL SELECT w.wk, w.rn +1 FROM WeekRowSkeleton w JOIN WeekMaxRn m ON w.wk = m.wk AND w.rn < m.max_rn ) SELECT wk, mon, tue, wed FROM ( SELECT s.wk, s.rn, t.day, t.subj FROM WeekRowSkeleton s LEFT JOIN mytable t ON s.wk = t.wk AND s.rn = t.rn ) d PIVOT ( MAX(subj) FOR day IN (mon, tue, wed) ) piv ORDER BY wk, rn;
内容的提问来源于stack exchange,提问作者emphyrio
相关产品推荐
相关产品推荐

