SQL Server:Datediff以分钟为单位,并考虑了自定义的银行假期,维护天数和工作时间

时间:2018-12-01 18:33:01

标签: sql sql-server

请忍受我。这已经long绕了,我希望我能清楚地遇到。

我有一个表(table1),其中包含数据类型为[deadline]的列[completion]datetime

我需要以分钟为单位计算[deadline][completion]之间的日期差异。

例如:

  • 截止日期= 2018-11-22 09:05
  • 完成= 2018-11-22 9:10

结果必须为5。

但是,我还需要使用以前生成的自定义表[tableBH]来计算银行假期,该表具有[date] [bank holiday name]

此外,还有另一个表维护表[tableMaintenance],其列为[date] [maintenance reason]

最后,我星期一至星期五的工作时间为0900至1900,星期六为1100-1500。

我需要datediff,最好以分钟为单位,以考虑非工作时间,非工作日和银行假期。

理想情况下是通过功能实现的。谁能指出从哪里开始?

我当前的脚本仅执行DATEDIFF ( mi , deadline, completion)

好吧,我这么说,但是目前有点令人费解,但是我以后可以解决。

为了保持透明度,我当前的代码(其中未考虑维护日,银行假期和非工作时间)是

CONVERT(varchar, Datediff(n, [deadline],[completion])/60) + ':' + Right('0' + CONVERT(varchar,(datediff(n,[deadline],[completion]) %60)),2)) as [TimeDIFF]

这种格式没有太大关系。

重要的是引用

  • 自定义银行假期表,而不是sys
  • 自定义维护表,
  • 工作时间(星期六和工作日不同)
  • 通过一个函数(在SQL Server 2017中)

帮助?

示例:

TableBH-[日期],[银行假期名称]列

26 November 2018, FakeBH1

TableMaintenance-列[日期],[维护原因]

28 November 2018, FakeMaintenance1

表1-[id],[deadline],[completion]列

  • 1,2018-11-24 14:55,2018-11-27 09:05
  • 2,2018-11-24 15:05,2018-11-27 09:04
  • 3,2018-11-27 11:00,2018-11-27 10:35
  • 4,2018-11-27 16:50,2018-11-29 09:10
  

输出ID,以分钟为单位的持续时间1 = 10 2 = 4 3 = -25 4 = 20

2 个答案:

答案 0 :(得分:0)

创建Bank_Calendar表,该表列出了每个银行日期,并关注工作时间。它包括关闭,休假和零时间维护。

BankDate    BankTimeStart       BankTimeEnd         BankDateDescription
2018-11-19  09:00:00.0000000    19:00:00.0000000    Mon
2018-11-20  09:00:00.0000000    19:00:00.0000000    Tue
2018-11-21  09:00:00.0000000    19:00:00.0000000    Wed
2018-11-22  09:00:00.0000000    19:00:00.0000000    Thu
2018-11-23  09:00:00.0000000    19:00:00.0000000    Fri
2018-11-24  11:00:00.0000000    15:00:00.0000000    Sat
2018-11-25  00:00:00.0000000    00:00:00.0000000    Sun closed
2018-11-26  00:00:00.0000000    00:00:00.0000000    Mon fakeBH1
2018-11-27  09:00:00.0000000    19:00:00.0000000    Tue
2018-11-28  00:00:00.0000000    00:00:00.0000000    Wed fakeMaintenance
2018-11-29  09:00:00.0000000    19:00:00.0000000    Thu
2018-11-30  09:00:00.0000000    19:00:00.0000000    Fri

这是我对table1的演绎,它具有与时间字段分开的日期字段,以使以后的运算更容易

DC_ID   Deadline_Date   Deadline_Time   Completion_Date Completion_Time
    1   2018-11-24  14:55:00.0000000    2018-11-27  09:05:00.0000000
    2   2018-11-24  15:05:00.0000000    2018-11-27  09:04:00.0000000
    3   2018-11-27  11:00:00.0000000    2018-11-27  10:35:00.0000000
    4   2018-11-27  16:50:00.0000000    2018-11-29  09:10:00.0000000
    5   2018-11-22  09:05:00.0000000    2018-11-22  09:10:00.0000000

这是SQL

select
    dc.*
    ,sum(
    Case when dc.Deadline_Date = cal.BankDate and dc.Completion_Date = cal.BankDate Then 
             --'SameDay'
             Datediff(n, dc.Deadline_Time, dc.Completion_Time)
          when dc.Deadline_Date = cal.BankDate and dc.Completion_Date <> cal.BankDate 
          and  dc.Deadline_Time > cal.BankTimeEnd Then
             --'FR' but Deadline is after Closing
             0 
          when dc.Deadline_Date = cal.BankDate and dc.Completion_Date <> cal.BankDate Then 
             --'FR' the remaining part of the day until closing
             Datediff(n, dc.Deadline_Time, cal.BankTimeEnd)
          when dc.Deadline_Date <> cal.BankDate and dc.Completion_Date <> cal.BankDate Then 
             --'allday' add the entire day  (for closings/holidays it becomes zero)
             Datediff(n, cal.BankTimeStart, cal.BankTimeEnd)
          when dc.Deadline_Date <> cal.BankDate and dc.Completion_Date = cal.BankDate Then 
             --'TO' add the beginning part of the day
             Datediff(n, cal.BankTimeStart, dc.Completion_Time)
      End )
      as minnn
from DateDiff_DeadlineCompletion as dc
Left Join DateDiff_Calendar as cal

On dc.Deadline_Date <= cal.BankDate
  and dc.Completion_Date >= cal.BankDate

Group By dc.DC_ID
         ,dc.Deadline_Date
         ,dc.Deadline_Time
         ,dc.Completion_Date
         ,dc.Completion_Time

和结果(顺便说一句-我们对项目4有了不同的答案)

DC_ID   Deadline_Date   Deadline_Time   Completion_Date Completion_Time minnn
1   2018-11-24  14:55:00.0000000    2018-11-27  09:05:00.0000000    10
2   2018-11-24  15:05:00.0000000    2018-11-27  09:04:00.0000000    4
3   2018-11-27  11:00:00.0000000    2018-11-27  10:35:00.0000000    -25
4   2018-11-27  16:50:00.0000000    2018-11-29  09:10:00.0000000    140
5   2018-11-22  09:05:00.0000000    2018-11-22  09:10:00.0000000    5

答案 1 :(得分:0)

BankDate    BankTimeStart       BankTimeEnd         WorkingMins
2018-11-19  09:00:00.0000000    19:00:00.0000000    720
2018-11-20  09:00:00.0000000    19:00:00.0000000    720
2018-11-21  09:00:00.0000000    19:00:00.0000000    720
2018-11-22  09:00:00.0000000    19:00:00.0000000    720
2018-11-23  09:00:00.0000000    19:00:00.0000000    720
2018-11-24  09:00:00.0000000    19:00:00.0000000    480
2018-11-25  09:00:00.0000000    19:00:00.0000000    480
2018-11-26  09:00:00.0000000    19:00:00.0000000    480 *(Bank Holiday)*
2018-11-27  09:00:00.0000000    19:00:00.0000000    720
2018-11-28  00:00:00.0000000    00:00:00.0000000    0 *(fakeMaintenance)*
2018-11-29  09:00:00.0000000    19:00:00.0000000    720
2018-11-30  09:00:00.0000000    19:00:00.0000000    720

上表是受@donPablo启发的

实际上,银行假日和星期日现在是上午9点至下午5点。

还有[IsBankHoliday]和[IsNonWorkingDay](通常是维护)两列。

然后我在DimDate过程中添加了以下内容:

CASE 
    WHEN [nw].[NonWorkingDayDate] IS  NOT NULL
        THEN '00:00:00'
    ELSE '09:00:00'
END AS [StartTime],


CASE 
    WHEN [nw].[NonWorkingDayDate] IS  NOT NULL
        THEN '00:00:00'
    WHEN [bh].[BankHolidayDate] IS  NOT NULL or [DayOfWeekNumber] IN (6,7)
        THEN '17:00:00'
    ELSE '21:00:00'
END AS [EndTime],

然后,我创建了以下函数:

ALTER FUNCTION [Udf].[GenerateBreachTime] (@CompletionAt [datetime],

@截止日期[日期时间])

SELECT (SELECT
  CASE
    WHEN @Deadline > @CompletionAt THEN -1
    WHEN CONVERT(varchar(8), @Deadline, 112) <= CONVERT(varchar(8), @CompletionAt, 112) THEN CASE   --NWD DC|| ||DC  = 0
        WHEN ([Non Working Day] = 1 AND
          CONVERT(varchar(8), @Deadline, 112) = CONVERT(varchar(8), [DateId], 112)) --nwd
          OR @Deadline > CAST(Concat(CONVERT(date, @Deadline), ' ', CONVERT(char(5), [EndTime], 108)) AS datetime) --deadline after hours
          OR @CompletionAt < CAST(Concat(CONVERT(date, @Deadline), ' ', CONVERT(char(5), [StartTime], 108)) AS datetime) THEN 0
        -- D|C|
        WHEN @Deadline < CAST(Concat(CONVERT(date, @Deadline), ' ', CONVERT(char(5), [StartTime], 108)) AS datetime) AND
          @CompletionAt < CAST(Concat(CONVERT(date, @Deadline), ' ', CONVERT(char(5), [EndTime], 108)) AS datetime) THEN DATEDIFF(n, CAST(Concat(CONVERT(date, @Deadline), ' ', CONVERT(char(5), [StartTime], 108)) AS datetime), @CompletionAt)
        --D||C
        WHEN @Deadline < CAST(Concat(CONVERT(date, @Deadline), ' ', CONVERT(char(5), [StartTime], 108)) AS datetime) AND
          @CompletionAt > CAST(Concat(CONVERT(date, @Deadline), ' ', CONVERT(char(5), [EndTime], 108)) AS datetime) THEN DATEDIFF(n, CAST(Concat(CONVERT(date, @Deadline), ' ', CONVERT(char(5), [StartTime], 108)) AS datetime), CAST(Concat(CONVERT(date, @Deadline), ' ', CONVERT(char(5), [EndTime], 108)) AS datetime))
        -- |DC|
        WHEN @Deadline >= CAST(Concat(CONVERT(date, @Deadline), ' ', CONVERT(char(5), [StartTime], 108)) AS datetime) AND
          @CompletionAt <= CAST(Concat(CONVERT(date, @Deadline), ' ', CONVERT(char(5), [EndTime], 108)) AS datetime) THEN DATEDIFF(n, @Deadline, @CompletionAt)
        --|D|C
        WHEN @Deadline >= CAST(Concat(CONVERT(date, @Deadline), ' ', CONVERT(char(5), [StartTime], 108)) AS datetime) AND
          @CompletionAt > CAST(Concat(CONVERT(date, @Deadline), ' ', CONVERT(char(5), [EndTime], 108)) AS datetime) THEN DATEDIFF(n, @Deadline, CAST(Concat(CONVERT(date, @Deadline), ' ', CONVERT(char(5), [EndTime], 108)) AS datetime))
        ELSE 0
      END
    ELSE 0
  END
  AS [BreachMins]
FROM [Vw].[Dim Date] DimDate
WHERE dateid = CONVERT(varchar(8), @Deadline, 112))
+ (SELECT
  CASE
    WHEN CONVERT(varchar(8), @Deadline, 112) >= CONVERT(varchar(8), @CompletionAt, 112) THEN 0
    WHEN CONVERT(varchar(8), @Deadline, 112) <= CONVERT(varchar(8), @CompletionAt, 112) THEN CASE
        -- C|| OR NWD
        WHEN @CompletionAt < CAST(Concat(CONVERT(date, @CompletionAt), ' ', CONVERT(char(5), [StartTime], 108)) AS datetime) OR
          ([Non Working Day] = 1 AND
          CONVERT(varchar(8), @CompletionAt, 112) = CONVERT(varchar(8), [DateId], 112)) THEN 0
        -- |C|
        WHEN @CompletionAt <= CAST(Concat(CONVERT(date, @CompletionAt), ' ', CONVERT(char(5), [EndTime], 108)) AS datetime)
        --THEN 1
        THEN DATEDIFF(n, CAST(Concat(CONVERT(date, @CompletionAt), ' ', CONVERT(char(5), [StartTime], 108)) AS datetime), @CompletionAt)
        --||C
        WHEN @CompletionAt > CAST(Concat(CONVERT(date, @CompletionAt), ' ', CONVERT(char(5), [EndTime], 108)) AS datetime)
        --THEN 2
        THEN DATEDIFF(n, CAST(Concat(CONVERT(date, @CompletionAt), ' ', CONVERT(char(5), [StartTime], 108)) AS datetime), CAST(Concat(CONVERT(date, @CompletionAt), ' ', CONVERT(char(5), [EndTime], 108)) AS datetime))
        ELSE 0
      END
    ELSE 0
  END
  AS [BreachMins]
FROM [Vw].[Dim Date] DimDate
WHERE dateid = CONVERT(varchar(8), @CompletionAt, 112))
+ (SELECT
  CASE
    WHEN SUM(workingminsinday) IS NULL THEN 0
    ELSE SUM(workingminsinday)
  END AS [x]
FROM vw.[Dim Date]
WHERE dateid BETWEEN CONVERT(varchar(8), @Deadline + 1, 112) AND CONVERT(varchar(8), @CompletionAt - 1, 112))

AS [BreachMins]

日常程序调用该函数

        ,[BreachTime].[BreachInMins]
    FROM [Syn].[X]

      CROSS APPLY [udf].[GenerateBreachTime] ([Completed],[Deadline]) [BreachTime]

后续视图处理-1种情况,将其更改为NULL。

我希望这对其他人有帮助,如果还不清楚的话,我们深表歉意。