【问题标题】:SQL - Get Minutes between 2 datetime but only for WorkdaysSQL - 获取 2 个日期时间之间的分钟数,但仅适用于工作日
【发布时间】:2015-09-14 14:21:12
【问题描述】:

我需要获取两个日期时间之间的分钟数。问题是我只需要计算一周中的分钟数。我的周末时间不算在内。另外,我们将周末定义为从周五下午 5 点开始,到周一上午 8 点结束。我相信我已经创建了一个函数来计算一周中的时间,但它没有考虑周五 17:00-23:59 和周一 00:00-7:59 之间的时间。此计算可能跨越数周,因此需要每周过滤掉这个时间。

一些预期结果的例子是:

  • 09/09/15 09:15 - 09/10/15 18:30 = 1995 分钟
  • 09/09/15 09:15 - 09/11/15 18:30 = 3345 分钟(柜台只到周五下午 5 点)
  • 09/09/15 09:15 - 09/12/15 18:30 = 3345 分钟
  • 09/09/15 09:15 - 09/13/15 18:30 = 3345 分钟
  • 09/09/15 09:15 - 09/14/15 18:30 = 3975 分钟(周一晚上 8 点开始计时)
  • 09/11/15 18:30 - 09/12/15 18:30 = 0 分钟(因为柜台在周五下午 5 点之后开始,直到周一早上 8 点才开始。
  • 09/12/15 18:30 - 09/14/15 18:30 = 630 分钟

这是目前为止的功能:

IF OBJECT_ID(N'WorkDaysMinutes', N'FN') IS NOT NULL
DROP FUNCTION [dbo].[WorkDaysMinutes]
GO
CREATE FUNCTION [dbo].[WorkDaysMinutes] (@StartDate DATETIME, @EndDate DATETIME = NULL) RETURNS FLOAT
AS
BEGIN
    DECLARE @Swap DATETIME
    DECLARE @Weekend_Start_Time NVARCHAR(5)
    DECLARE @Weekend_End_Time NVARCHAR(5)

    SET @Weekend_Start_Time = '17:00'
    SET @Weekend_End_Time = '08:00'

    IF @StartDate IS NULL
        RETURN NULL

    IF @EndDate IS NULL
        SELECT @EndDate = @StartDate

    IF @StartDate > @EndDate
        SELECT @Swap      = @EndDate,
               @EndDate   = @StartDate,
               @StartDate = @Swap

    RETURN (
        SELECT
    --Start with total number of minutes including weekends
        DATEDIFF(MI,@StartDate, @EndDate)
    --Subtact 2880 minutes for each full weekend
        -(DATEDIFF(wk, DATEADD(dd,-1,@StartDate), DATEADD(dd,-1,@EndDate)) * 2880)
    --If StartDate is a Weekend, Subtract time untill monday
        -(CASE WHEN DATENAME(dw, @StartDate) = 'Sunday' THEN DATEDIFF(MI,@StartDate,DATEADD(d,0,DATEDIFF(d,-1,@StartDate))) ELSE 0 END)
        -(CASE WHEN DATENAME(dw, @StartDate) = 'Saturday' THEN DATEDIFF(MI,@StartDate,DATEADD(d,0,DATEDIFF(d,-1,@StartDate))) + 1440 ELSE 0 END)
    --If EndDate is a Weekend, Subtract time since friday
        -(CASE WHEN DATENAME(dw, @EndDate) = 'Saturday' THEN DATEDIFF(mi,CONVERT(varchar(10),@EndDate,112),@EndDate) ELSE 0 END)
        -(CASE WHEN DATENAME(dw, @EndDate) = 'Sunday' THEN DATEDIFF(mi,CONVERT(varchar(10),@EndDate,112),@EndDate) + 1440 ELSE 0 END)
    --Subtract all holidays
        -((SELECT Count(*) 
           FROM SY_Holiday H
           WHERE H.Date BETWEEN @StartDate AND @EndDate
            AND DATEPART(DW,H.Date) NOT IN (6,7)) * 1440)
        )
    END  
GO

编辑:我们还需要考虑假期。这些假期存储在表 SY_Holiday 中。如果假期在周六或周日,则无关紧要,不应扣除,但如果假期在周二至周四,我们将每天扣除 1440。如果假期是在星期五,那么我们需要从星期四下午 5 点到星期五下午 5 点之间扣除时间。这同样适用于星期一。这应该只是意味着减去一个 1440 应该没问题。

【问题讨论】:

  • 国定假日等非工作日呢?
  • 谢谢你提醒我,我忘了说,这就是我们的假期表。这将跟踪那些日子,它应该从总数中扣除。我已经编辑了原始帖子以突出显示假期。
  • 与其仅仅实现一个holiday表,为什么不引入一个calendar表呢?每天一行,20 年的行数只有 7000 行。您可以添加有用的列,例如每天工作的开始时间和结束时间,然后将此查询简化为仅从该表中查找与给定日期重叠的行,并对找到的每一天的工作时间求和。
  • 好吧,我咬牙切齿....我们将如何进行比较?

标签: sql sql-server-2008 tsql


【解决方案1】:

这对我有用:

DECLARE @Swap DATETIME2
DECLARE @Weekend_Start_Time time = '17:00'
DECLARE @Weekend_End_Time time = '08:00'

IF @StartDate > @EndDate
    Select  @Swap = @EndDate, @EndDate = @StartDate, @StartDate = @Swap

    Set @StartDate = DATEADD(DAY, -1, DATEADD(MINUTE, -DATEPART(MINUTE, @Weekend_End_Time), DATEADD(HOUR, -DATEPART(HOUR, @Weekend_End_Time), @StartDate)))
    Set @EndDate = DATEADD(DAY, -1, DATEADD(MINUTE, -DATEPART(MINUTE, @Weekend_End_Time), DATEADD(HOUR, -DATEPART(HOUR, @Weekend_End_Time), @EndDate)))
    Set @Weekend_Start_Time = DATEADD(MINUTE, -DATEPART(MINUTE, @Weekend_End_Time), DATEADD(HOUR, -DATEPART(HOUR, @Weekend_End_Time), @Weekend_Start_Time))

SELECT
    --Start with total number of minutes including weekends
    DATEDIFF(MINUTE,@StartDate, @EndDate)
    --Subtact 2880 minutes for each full weekend
    - (DATEDIFF(wk, @StartDate, @EndDate) * 3780)
    --If StartDate is a Weekend, Subtract time untill monday
    - Coalesce(DATEDIFF(MINUTE,
        DATEADD(HOUR, DATEPART(HOUR, @Weekend_Start_Time) - DATEPART(HOUR, @StartDate),
            DATEADD(MINUTE, DATEPART(MINUTE, @Weekend_Start_Time) - DATEPART(MINUTE, @StartDate),
                DATEADD(d, -Case DATEPART(dw, @StartDate) When 5 then Case When cast(@StartDate as time) > @Weekend_Start_Time then 0 end
                    When 6 then 1 When 7 then 2 End, @StartDate)
                )
            )
        , @StartDate), 0)
    --If EndDate is a Weekend, Subtract time since friday
    - Coalesce(DATEDIFF(MINUTE, 
        DATEADD(HOUR, DATEPART(HOUR, @Weekend_Start_Time) - DATEPART(HOUR, @EndDate),
            DATEADD(MINUTE, DATEPART(MINUTE, @Weekend_Start_Time) - DATEPART(MINUTE, @EndDate),
                DATEADD(d, -Case DATEPART(dw, @EndDate) When 5 then Case When cast(@EndDate as time) > @Weekend_Start_Time then 0 end
                    When 6 then 1 When 7 then 2 End, @EndDate)
                )
            )
        , @EndDate), 0)

请注意,我将 2880 更改为 3780。这是从星期五 17:00 到星期一 8:00 的分钟数

【讨论】:

  • 感谢您的帮助,如果开始和结束日期在周末窗口之外,这会很好。但是,如果我们在窗口内开始,计算就会变成负数。
  • 例如 '09/11/15 18:30' 到 '09/12/15 18:30' 应该返回 0 天,但我得到了 -90。
  • 必须为 case 6 的开始日期和 case=2 的开始日期添加一个额外的条件。今晚晚些时候我会更新我的答案。
  • 谢谢Julien,还要注意假期的要求,我已经更新了原始代码以显示假期逻辑。这与你的数学兼容吗?
  • 可以添加假日表。这不应该是一个问题。我只是删除它,因为在这里测试它更容易。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-17
  • 1970-01-01
  • 1970-01-01
  • 2019-06-19
相关资源
最近更新 更多