根据重叠的 date/time 数据计算 T-SQL 中的每周运行小时数?

Calculate weekly hours of operation in T-SQL from overlapping date/time data?

我正在尝试计算某个地区每个设施每周的运营小时数。我苦苦挣扎的部分是每天有多个项目重叠,这会影响总时数。

这是我正在使用的 table 的示例:

location program date start_time end_time
a 1 09-22-21 14:45:00 15:45:00
a 2 09-22-21 15:30:00 16:30:00
b 88 09-22-21 10:45:00 12:45:00
b 89 09-22-21 10:45:00 14:45:00

我希望得到:

location hours of operation
a 1.75
b 4

我试过将 SUM DATEDIFF 与某些 WHERE 语句一起使用,但无法使它们起作用。我发现的是如何识别重叠范围 (Detect overlapping date ranges from the same table),而不是如何对差异求和以获得总非重叠运行小时数的预期结果。

相信您正在尝试确定每个位置的总营业时间。现在因为一些程序可以重叠,你想排除这些。为此,我生成了日期中每个可能的 15 分钟增量的计数 table,然后计算程序运行的时间段

确定每个日期的总营业时间

DROP TABLE IF EXISTS #OperationSchedule
CREATE TABLE #OperationSchedule (ID INT IDENTITY(1,1),Location CHAR(1),Program INT,OpDate DATE,OpStart TIME(0),OpEnd TIME(0))
INSERT INTO #OperationSchedule
VALUES ('a',1,'09-22-21','14:45:00','15:45:00')
,('a',2,'09-22-21','15:30:00','16:30:00')
,('b',88,'09-22-21','10:45:00','12:45:00')
,('b',89,'09-22-21','10:45:00','14:45:00');

/*1 row per 15 minute increment in a day*/
;WITH cte_TimeIncrement AS (
    SELECT StartTime = CAST('00:00' AS TIME(0))
    UNION ALL
    SELECT DATEADD(minute,15,StartTime)
    FROM cte_TimeIncrement
    WHERE StartTime < '23:45'
),
/*1 row per date in data*/
cte_DistinctDate AS (
    SELECT OpDate
    FROM #OperationSchedule
    GROUP BY OpDate
),
/*Cross join to generate 1 row for each time increment*/
cte_DatetimeIncrement AS (
SELECT *
FROM cte_DistinctDate
CROSS JOIN cte_TimeIncrement
)

/*Join and count each time interval that has a match to identify times when location is operating*/
SELECT Location
    ,A.OpDate
    ,HoursOfOperation = CAST(COUNT(DISTINCT StartTime) * 15/60.0 AS Decimal(4,2))
FROM cte_DatetimeIncrement AS A
INNER JOIN #OperationSchedule AS B
    ON A.OpDate = B.OpDate
    AND A.StartTime >= B.OpStart
    AND A.StartTime < B.OpEnd
GROUP BY Location,A.OpDate

这是一种替代方法,无需四舍五入到最接近的 15 分钟增量:

Declare @OperationSchedule table (
        ID       int Identity(1, 1)
      , Location char(1)
      , Program  int
      , OpDate   date
      , OpStart  time(0)
      , OpEnd    time(0)
        );

 Insert Into @OperationSchedule (Location, Program, OpDate, OpStart, OpEnd)
 Values ('a',  1, '09-22-21', '14:45:00', '15:45:00')
      , ('a',  2, '09-22-21', '15:30:00', '16:30:00')
      , ('b', 88, '09-22-21', '10:45:00', '12:45:00')
      , ('b', 89, '09-22-21', '10:45:00', '14:45:00')
      , ('c', 23, '09-22-21', '12:45:00', '13:45:00')
      , ('c', 24, '09-22-21', '14:45:00', '15:15:00')
      , ('3', 48, '09-22-21', '09:05:00', '13:55:00')
      , ('3', 49, '09-22-21', '14:25:00', '15:38:00')
      ;

   With overlappedData
     As (
 Select *
      , overlap_op = lead(os.OpStart, 1, os.OpEnd) Over(Partition By os.Location Order By os.ID)
   From @OperationSchedule os
        )
 Select od.Location
      , start_date = min(od.OpStart)
      , end_date = max(iif(od.OpEnd < od.overlap_op, od.OpEnd, od.overlap_op))
      , hours_of_operation = sum(datediff(minute, od.OpStart, iif(od.OpEnd < od.overlap_op, od.OpEnd, od.overlap_op)) / 60.0)
   From overlappedData                  od
  Group By
        od.Location;