SQL 按行到列的数据透视组

SQL Pivot group by rows to columns

所以我有一个 table 用于我想每天计算的司机活动。 示例数据:

Name StartDateAndTime EndDateAndTime StartText EndText WorkState
Jim 2021-09-09 06:28:16 2021-09-09 07:28:16 "StartPlace0" "EndPlace0" "Pause"
Jim 2021-09-09 07:28:16 2021-09-09 08:28:16 "StartPlace1" "EndPlace1" "Loading"
Jim 2021-09-09 08:28:16 2021-09-09 09:28:16 "StartPlace2" "EndPlace2" "Driving"
Jim 2021-09-09 10:28:16 2021-09-09 11:28:16 "StartPlace4" "EndPlace4" "Loading"
Jim 2021-10-09 09:28:16 2021-10-09 10:28:16 "StartPlace5" "EndPlace5" "Driving"
Jim 2021-10-09 10:28:16 2021-10-09 11:28:16 "StartPlace6" "EndPlace6" "Loading"
Jim 2021-10-09 11:28:16 2021-10-09 14:28:16 "StartPlace7" "EndPlace7" "Driving"

期望的输出:(我只有 3 种可能的工作状态)

Name StartDateAndTime EndDateAndTime StartText EndText Loading Driving
Jim 2021-09-09 06:28:16 2021-09-09 11:28:16 "StartPlace0" "EndPlace4" 00:02:00 00:01:00
Jim 2021-10-09 09:28:16 2021-09-09 14:28:16 "StartPlace5" "EndPlace7" 00:01:00 00:04:00
Pause DayPerformed
00:01:00 2021-09-09
00:00:00 2021-10-09

到目前为止我的看法:(摘要)

 --CREATE view vwWorkingTimesPerDay as
select  
   w.DriverName, 
   convert(date, w.StartDateAndTime) as DayPerformed,
   MIN(w.StartDateAndTime) as StartTime,
   MAX(w.EndDateAndTime) as EndTime,
   StartPositionText,
   EndPositionText

from  ( 
  SELECT *,
  StartPositionText = FIRST_VALUE(StartPosText) OVER (PARTITION BY w.DriverName, convert(date, 
  w.StartDateAndTime)
        ORDER BY w.StartDateAndTime ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING),
  EndPositionText   = LAST_VALUE(EndPosText) OVER (PARTITION BY w.DriverName, convert(date, 
  w.StartDateAndTime)
        ORDER BY w.StartDateAndTime ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
FROM webfleet.tblWorkingTimes w) w

  group by w.DriverName, 
     convert(date, w.StartDateAndTime),
     StartPositionText,
     EndPositionText,
   

为了显示原始 table 中的持续时间,我使用了:

right('00' + convert(varchar, datepart(hour, dateadd(s, sum(datediff(second, 
    w.StartDateAndTime, w.EndDateAndTime)), 0))), 2) + ':'
 + right('00' + convert(varchar, datepart(minute, dateadd(s, sum(datediff(second, 
  w.StartDateAndTime, w.EndDateAndTime)), 0))), 2) + ':'
  + right('00' + convert(varchar, datepart(second, dateadd(s, sum(datediff(second, 
  w.StartDateAndTime, w.EndDateAndTime)), 0))), 2) 
   as Duration

我试过这篇文章https://www.sqlshack.com/multiple-options-to-transposing-rows-into-columns

但我没有足够的经验来使用这些选项。 我想要的数据顺序并不重要,如果有更好的方法来计算和显示持续时间,我很高兴听到。

这需要几个步骤

  • 我们使用CAST AS date来获取没有时间的日期
  • ROW_NUMBERLEAD帮助我们识别每个分组的开始和结束
  • 然后简单地按 Name 和日期
  • 汇总
  • 并使用条件聚合得到我们想要的结果
  • 要获得时间值,我们将秒数加到 0:00:00 时间
SELECT
  Name,
  DayPerformed,
  StartDateAndTime = MIN(StartDateAndTime),
  EndDateAndTime = MAX(EndDateAndTime),
  StartText = MIN(CASE WHEN rn = 1 THEN StartText END),
  EndText = MIN(CASE WHEN nxt IS NULL THEN EndText END),
  Loading = DATEADD(ms,
      ISNULL(SUM(CASE WHEN WorkState = 'Loading' THEN DATEDIFF(ms, StartDateAndTime, EndDateAndTime) END), 0),
      CAST('0:00:00' AS time)),
  Driving = DATEADD(ms, 
      ISNULL(SUM(CASE WHEN WorkState = 'Driving' THEN DATEDIFF(ms, StartDateAndTime, EndDateAndTime) END), 0),
      CAST('0:00:00' AS time)),
  Pause   = DATEADD(ms,
      ISNULL(SUM(CASE WHEN WorkState = 'Pause'   THEN DATEDIFF(ms, StartDateAndTime, EndDateAndTime) END), 0), 
      CAST('0:00:00' AS time))
FROM (
    SELECT *,
      rn = ROW_NUMBER() OVER (PARTITION BY Name, DayPerformed ORDER BY StartDateAndTime),
      nxt = LEAD(WorkState) OVER (PARTITION BY Name, DayPerformed ORDER BY StartDateAndTime)
    FROM tblWorkingTimes wt
    CROSS APPLY (VALUES (CAST(StartDateAndTime AS date))) v(DayPerformed)
) wt
GROUP BY
  Name,
  DayPerformed;

要按月汇总,只需将 DayPerformed 替换为

CROSS APPLY (VALUES (EOMONTH(StartDateAndTime))) v(DayPerformed)

db<>fiddle