将DATEDIFF分成不同的月份

时间:2019-07-26 18:42:50

标签: sql sql-server datediff

我目前遇到一个问题,即我要在开始日期和结束日期之间的分钟内执行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

然后,这将给我仅在开始日期和结束日期之间的那个月的总分钟数。

非常感谢任何帮助。

3 个答案:

答案 0 :(得分:2)

编辑:递归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

返回:

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

答案 1 :(得分:0)

要实现此目的,您将需要创建一个额外的表以连接到包含月的表。然后,您要加入该日期所在的表,该日期所在的月在日历表中的日期之间,为此,您需要使用dateadd / datediff函数将日期四舍五入到月的第一天,例如:{{1} }。通过计算某个随机开始日期(在本例中为0,即1/1/1900)之间的月份差,然后将这些月份加回到开始日期,可以起作用。

然后,如果它们与日历表记录不在同一月份,则需要将开始或结束日期向上或向下四舍五入到月底,这将允许您对该时间进行新的计算。

整个代码如下:

DATEADD(month, DATEDIFF(month, 0, StartDate),0)

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

答案 2 :(得分:0)

这可以使用递归CTE如下实现。这将计算开始日期和结束日期之间的多个月。 小提琴: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