将 DATEDIFF 拆分为单独的月份

Split DATEDIFF into separate months

我目前有一个问题,我在开始日期和结束日期之间的几分钟内执行 DATEDIFF,但是当这个日期进入新的月份时,我需要每个月的数字分开。

请查看示例数据(文本和图像视图);

SELECT [BookingNum]
  ,[StartDate]
  ,[EndDate]
  ,[Location]
  ,DATEPART(m,startdate) AS [Month]
  ,DATEDIFF(MINUTE,StartDate,EndDate) AS [Minutes]
FROM [Test].[dbo].[Booking]


BookingNum  StartDate                  EndDate        Location Month Minutes
   1    2019-02-05 12:54:00.000 2019-02-08 15:00:00.000 Area 1  2   4446
   2    2019-05-02 10:41:00.000 2019-05-10 12:39:00.000 Area 2  5   11638
   3    2019-06-01 10:30:00.000 2019-06-04 09:25:00.000 Area 3  6   4255
   4    2019-02-02 09:41:00.000 2019-04-20 11:54:00.000 Area 1  2   111013
   5    2019-03-29 19:09:00.000 2019-04-02 10:41:00.000 Area 3  3   5252

第 4 行和第 5 行需要额外的行,因为它们跨越多个月。

第 4 行数据示例,我想看看;

StartDate                  EndDate            Location Month Minutes
2019-02-02 09:41:00.000 2019-02-28 23:59:00.000 Area 1  2   38298
2019-03-01 00:00:00.000 2019-03-31 23:59:00.000 Area 1  3   44639
2019-04-01 00:00:00.000 2019-04-20 23:59:00.000 Area 1  4   28074

这将仅在开始日期和结束日期之间给出该月的总分钟数。

非常感谢任何帮助。

编辑:递归 CTE 应该可以解决问题!基本上,使用递归通过 EOM 和结束日期中较小的那个开始日期,直到最终到达结束日期。

Fiddle

DECLARE @tbl TABLE (bookingnum INT, sd DATETIME, ed DATETIME)
INSERT INTO @tbl VALUES 
(1, '2/5/2019 12:54 PM', '2/8/2019 3:00 PM'),
(2, '5/2/2019 10:41 AM', '5/10/2019 12:39 PM'),
(3, '6/1/2019 10:30 AM', '6/4/2019 9:25 AM'),
(4, '2/2/2019 9:41 AM', '5/20/2019 11:54 AM'),
(5, '3/29/2019 7:09 PM', '4/2/2019 10:41 AM')

;WITH cte AS (
    SELECT bookingnum, sd, DATEADD(DAY, 1, EOMONTH(sd)) eom, ed,
        CASE WHEN DATEADD(DAY, 1, EOMONTH(sd)) < ed THEN DATEADD(DAY, 1, EOMONTH(sd)) else ed END AS applied_ed
    FROM @tbl
    UNION ALL 
    SELECT bookingnum, applied_ed, DATEADD(DAY, 1, EOMONTH(applied_ed)) eom, ed,
        CASE WHEN DATEADD(DAY, 1, EOMONTH(applied_ed)) < ed THEN DATEADD(DAY, 1, EOMONTH(applied_ed)) else ed END AS applied_ed
    FROM cte
    WHERE applied_ed < ed
)
SELECT bookingnum, sd, applied_ed AS ed, DATEDIFF(MINUTE, sd, applied_ed) minutes
FROM cte
ORDER BY bookingnum, sd

Returns:

bookingnum  sd                      ed                          minutes
1           2019-02-05 12:54:00.000 2019-02-08 15:00:00.000     4446
2           2019-05-02 10:41:00.000 2019-05-10 12:39:00.000     11638
3           2019-06-01 10:30:00.000 2019-06-04 09:25:00.000     4255
4           2019-02-02 09:41:00.000 2019-03-01 00:00:00.000     38299
4           2019-03-01 00:00:00.000 2019-04-01 00:00:00.000     44640
4           2019-04-01 00:00:00.000 2019-05-01 00:00:00.000     43200
4           2019-05-01 00:00:00.000 2019-05-20 11:54:00.000     28074
5           2019-03-29 19:09:00.000 2019-04-01 00:00:00.000     3171
5           2019-04-01 00:00:00.000 2019-04-02 10:41:00.000     2081

要实现此目的,您需要创建一个额外的 table 以加入其中包含月份。然后,您将加入 table,其中日期的月份在日历 table 中的日期之间,为此,您需要使用 dateadd/datediff 函数将日期四舍五入为本月的第一天,例如:DATEADD(month, DATEDIFF(month, 0, StartDate),0)。这通过计算一些随机开始日期(在本例中为 0,即 1/1/1900)之间的月份差异,然后将这些月份添加回开始日期来工作。

然后,如果开始日期或结束日期与日历 table 记录不在同一月份,则需要将开始日期或结束日期向上或向下舍入到月末,这样您就可以执行当时的新计算。

整个代码看起来像这样:

CREATE TABLE #MonthDate
(MonthDate date PRIMARY KEY);

INSERT INTO #MonthDate (MonthDate)
VALUES ('20190101'),('20190201'),('20190301'),('20190401'),('20190501'),('20190601');

WITH RoundedDates As 
(SELECT b.StartDate,
B.EndDate,
DATEADD(month, DATEDIFF(month, 0, b.StartDate),0)  AS RoundedStartDate,
DATEADD(month, DATEDIFF(month, 0, b.EndDate),0)  AS RoundedEndDate
FROM Test.dbo.Booking AS b)
SELECT rd.StartDate
, rd.EndDate
, DATEDIFF(minute, CASE WHEN rd.RoundedStartDate = md.MonthDate THEN rd.StartDate ELSE md.MonthDate END, CASE WHEN rd.RoundedEndDate = md.MonthDate THEN rd.EndDate ELSE DATEADD(month,1,md.MonthDate) END) AS Minutes
FROM RoundedDates AS rd
INNER JOIN #MonthDate as md
   ON md.MonthDate BETWEEN rd.RoundedStartDate AND rd.RoundedEndDate

http://sqlfiddle.com/#!18/70730/2

这可以使用递归 CTE 实现,如下所示。这将计算开始日期和结束日期之间的多个月份。 Fiddle: http://sqlfiddle.com/#!18/26568/4

create table #temp(
BookingNum int,
StartDate datetime,
EndDate datetime,
Location varchar(25),
)

insert into #temp
values(1,'2019-02-05 12:54:00','2019-02-08 15:00:00','Area 1'),
(2,'2019-05-02 10:41:00','2019-05-10 12:39:00','Area 2'),
(3,'2019-06-01 10:30:00','2019-06-04 09:25:00','Area 3'),
(4,'2019-02-02 09:41:00','2019-05-20 11:54:00','Area 1'),
(5,'2019-03-29 19:09:00','2019-04-02 10:41:00','Area 3')


;WITH cte AS
  (
    SELECT BookingNum,
         StartDate,
         CASE
            WHEN DATEPART(m, EndDate) > DATEPART(m, startdate)
            THEN DATEADD(s, -1, DATEADD(mm, DATEDIFF(m, 0, startdate) + 1, 0))
            ELSE EndDate
         END AS EndDate,
         Location,
         DATEPART(m, EndDate) - DATEPART(m, startdate) AS MonthDiff
    FROM #temp

    UNION ALL

    SELECT cte.BookingNum,
         CASE
            WHEN cte.MonthDiff > 0
            THEN DATEADD(month, DATEDIFF(month, 0, DATEADD(month, 1, cte.StartDate)), 0)
            ELSE cte.StartDate
         END AS startDate,
         CASE
            WHEN cte.MonthDiff > 0  AND DATEADD(d, -1, DATEADD(m, DATEDIFF(m, 0, DATEADD(month, 1, cte.StartDate)) + 1, 0)) < t.EndDate
            THEN DATEADD(d, -1, DATEADD(m, DATEDIFF(m, 0, DATEADD(month, 1, cte.StartDate)) + 1, 0))
            ELSE t.EndDate
         END AS EndDate,
         cte.Location,
         (cte.MonthDiff - 1) MonthDiff
    FROM cte
        INNER JOIN #temp t ON cte.BookingNum = t.BookingNum
    WHERE cte.MonthDiff > 0
)
SELECT BookingNum,
      StartDate,
       EndDate,
       Location,
       DATEPART(m, startdate) AS month,
       DATEDIFF(minute, startdate, enddate) AS minutes  
FROM cte
ORDER BY 1;



drop table #temp

结果:

BookingNum  StartDate               EndDate                 Location                  month       minutes
----------- ----------------------- ----------------------- ------------------------- ----------- -----------
1           2019-02-05 12:54:00.000 2019-02-08 15:00:00.000 Area 1                    2           4446
2           2019-05-02 10:41:00.000 2019-05-10 12:39:00.000 Area 2                    5           11638
3           2019-06-01 10:30:00.000 2019-06-04 09:25:00.000 Area 3                    6           4255
4           2019-02-02 09:41:00.000 2019-02-28 23:59:59.000 Area 1                    2           38298
4           2019-03-01 00:00:00.000 2019-03-31 00:00:00.000 Area 1                    3           43200
4           2019-04-01 00:00:00.000 2019-04-30 00:00:00.000 Area 1                    4           41760
4           2019-05-01 00:00:00.000 2019-05-20 11:54:00.000 Area 1                    5           28074
5           2019-03-29 19:09:00.000 2019-03-31 23:59:59.000 Area 3                    3           3170
5           2019-04-01 00:00:00.000 2019-04-02 10:41:00.000 Area 3                    4           2081