使用 SQL 将小时分成多个月

Split Hours into Multiple Months using SQL

CREATE TABLE EventLog 
(
  EventID INT
, EventName VARCHAR(50) NOT NULL
, EventStartDateTime DATETIME NOT NULL
, EventEndDateTime   DATETIME NULL 
)


INSERT INTO EventLog(EventID, EventName, EventStartDateTime, EventEndDateTime)
VALUES(100, 'Planting', '20210620 10:34:09 AM','20211018 10:54:49 PM')
 ,(200, 'Foundation', '20200420 10:34:09 AM','20211018 10:54:49 PM')
 ,(300, 'Seeding', '20210410 10:27:19 AM','')
 ,(400, 'Spreading', '20220310 10:24:09 PM','')

我需要根据事件的长度将小时分成几个月甚至几年。在某些情况下,活动可能有结束日期,但如果活动仍在进行中,则不会有结束日期。

解决方案的结果或输出将由另一个 table:

CREATE TABLE EventSummary
(  
  EventID INT
, EventName VARCHAR(50) NOT NULL
, [Year] INT
, [MonthName] VARCHAR(25) 
, [Hours] DECIMAL(12,2) 
)

上图是第一行的输出。

如果事件持续多年,则值也应同样分布在多个年份和月份中。 在没有结束日期的情况下,我将使用 GETUTCDATE() 进行计算。 有些事件跨越数月或事件年。我希望能够分别以小时为单位将总持续时间分解为单个月的持续时间(或按月计算的单个持续时间),并将其填充到 table

假设我有一个开始和结束日期的活动:'20210620 10:34:09 AM','20211018 10:54:49 PM' 对于基本上不是完整月份的第一个月,我要计算该月的剩余时间并将其存储在该月。 下个月我也会这样做。如果事件运行整个月,即现在的 7 月,我将存储该月的整个小时数,即 7 月的 744 小时。我一直这样做,直到活动结束。但是如果事件仍然是开放的(空白或空的)我使用 GETUTCDATE() 作为结束日期 Hours 的总和按 EventID、EventName、Year 和 Months 分组 预计第一个月和/或最后一个月可能是小数,因为它们可能不完整。

我已经尝试解决这个问题,但不知道如何使用 SQL 服务器获得最佳结果。 非常感谢您对此的帮助。 谢谢

如果我理解正确,你可以尝试使用 CTE 递归,获取每个月的开始日期然后使用 antoher cte2 获取每个月之间的开始时间和结束时间。

;WITH CTE AS (
   SELECT EventID,EventName,EventStartDateTime,IIF(EventEndDateTime = '',GETUTCDATE(),EventEndDateTime) EventEndDateTime
   FROM EventLog
   UNION ALL
   SELECT EventID,EventName, DATEADD(month, DATEDIFF(month, 0, DATEADD(month , 1 , EventStartDateTime)), 0) , EventEndDateTime
   FROM CTE 
   WHERE  DATEADD(month, DATEDIFF(month, 0, DATEADD(month , 1 , EventStartDateTime)), 0)  <= EventEndDateTime
), CTE2 AS (
   SELECT EventID,EventName,EventStartDateTime,LEAD(EventStartDateTime,1,EventEndDateTime) OVER(PARTITION BY EventID,EventName ORDER BY EventStartDateTime) n_EventStartDateTime
   FROM CTE
)
INSERT INTO EventSummary(EventID,EventName,Year,MonthName,Hours)
SELECT EventID,EventName,YEAR(EventStartDateTime),DATENAME(MONTH,EventStartDateTime),DATEDIFF(second, EventStartDateTime, n_EventStartDateTime) / 3600.0
FROM CTE2
option (maxrecursion 0)

sqlfiddle