如何将SQL查询结果按活动分组到同一行?
解决方案:合并同一活动的每日工时到单行
看起来你想要把同一活动下的每日工时记录合并到同一行,而不是每个单独的时间条目都占一行。咱们可以通过字符串聚合函数和调整分组逻辑来实现这个需求,具体修改方案如下:
原查询的问题分析
你的原查询在GROUP BY里包含了WorkDate和[Time],这会导致每一条时间记录都被单独分组,从而生成多行结果。要实现按活动聚合,需要去掉这两个分组字段,同时用聚合函数把同一天的多个时间合并成一个字符串。
修改后的SQL查询(适用于SQL Server 2017+)
SELECT Employee, Departmentname, [Week], Activity, -- 合并周一的所有时间,用逗号分隔并按时间排序 STRING_AGG(CASE WHEN DATENAME(dw, WorkDate) = 'MONDAY' THEN CONVERT(VARCHAR(5), [Time], 108) END, ', ') WITHIN GROUP (ORDER BY [Time]) AS Monday, -- 合并周二的所有时间 STRING_AGG(CASE WHEN DATENAME(dw, WorkDate) = 'TUESDAY' THEN CONVERT(VARCHAR(5), [Time], 108) END, ', ') WITHIN GROUP (ORDER BY [Time]) AS Tuesday, -- 合并周三的所有时间 STRING_AGG(CASE WHEN DATENAME(dw, WorkDate) = 'WEDNESDAY' THEN CONVERT(VARCHAR(5), [Time], 108) END, ', ') WITHIN GROUP (ORDER BY [Time]) AS Wednesday, -- 合并周四的所有时间 STRING_AGG(CASE WHEN DATENAME(dw, WorkDate) = 'THURSDAY' THEN CONVERT(VARCHAR(5), [Time], 108) END, ', ') WITHIN GROUP (ORDER BY [Time]) AS Thursday, -- 合并周五的所有时间 STRING_AGG(CASE WHEN DATENAME(dw, WorkDate) = 'FRIDAY' THEN CONVERT(VARCHAR(5), [Time], 108) END, ', ') WITHIN GROUP (ORDER BY [Time]) AS Friday, -- 合并周六的所有时间 STRING_AGG(CASE WHEN DATENAME(dw, WorkDate) = 'SATURDAY' THEN CONVERT(VARCHAR(5), [Time], 108) END, ', ') WITHIN GROUP (ORDER BY [Time]) AS Saturday, -- 计算该活动的总工时,格式化为HH:MM:SS CONVERT(VARCHAR(8), DATEADD(millisecond, SUM(DATEDIFF(millisecond, 0, CAST([Time] AS DATETIME))), 0), 108) AS TotalHRW FROM TimeSheet WHERE [Week] = 18 -- 用=代替LIKE更合适,因为Week是数值类型的精确匹配 AND Departmentname = 'Returns' AND Employee = 'Hand Batis' GROUP BY Employee, Departmentname, [Week], Activity -- 仅按核心维度分组,确保同一活动聚合为一行 ORDER BY [Week], Employee
关键修改点说明
- 字符串聚合:用
STRING_AGG函数把同一活动、同一工作日的所有时间合并成逗号分隔的字符串,WITHIN GROUP (ORDER BY [Time])还能保证时间按先后顺序排列。 - 分组调整:
GROUP BY只保留Employee, Departmentname, [Week], Activity,确保同一活动的所有记录被聚合到单行。 - 总工时计算:调整总工时逻辑,对该活动下所有时间的毫秒数求和后再转换为标准时间格式,结果更准确。
- 条件优化:把
LIKE替换为=,因为你的筛选是精确匹配,LIKE在这里不仅没必要,还可能影响查询性能。
兼容旧版SQL Server(2016及更早)的写法
如果你的SQL Server版本不支持STRING_AGG,可以用STUFF+FOR XML PATH的方式实现字符串合并,以周一列为例:
STUFF(( SELECT ', ' + CONVERT(VARCHAR(5), [Time], 108) FROM TimeSheet t2 WHERE t2.Employee = t1.Employee AND t2.Departmentname = t1.Departmentname AND t2.[Week] = t1.[Week] AND t2.Activity = t1.Activity AND DATENAME(dw, t2.WorkDate) = 'MONDAY' ORDER BY t2.[Time] FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)'), 1, 2, '') AS Monday
需要把每个工作日的列都替换成这种写法,GROUP BY和总工时计算逻辑保持不变。
内容的提问来源于stack exchange,提问作者Tony Batista
相关产品推荐
相关产品推荐

