【问题标题】:Calculating working hours between two dates - Including weekends计算两个日期之间的工作时间 - 包括周末
【发布时间】:2020-03-05 18:36:55
【问题描述】:

我的任务是计算从首次输入订单到发货所需的工作小时数。为此,由于权限问题,我无法创建任何类型的函数。

营业时间为周一至周五 08:00 - 17:30。周六和周日 - 08:00 - 16:00。它需要合计在这些核心小时内花费的时间。

如果订单是在当天开始(上午 8 点)之前下达的,则需要将订单视为上午 8 点的输入。如果订单是在一天结束之后(5.30pm M-F,4pm S/SS),则视为订单是在一天结束时输入的。 交货也是如此。

我对此进行了搜索,并在 SO 上找到了以前的答案,但发现所有以前接受的答案都使用函数来执行此操作。

在我的这个开发中,我已经在不使用函数的情况下实现了这一点。我在下面分享我的答案以供将来搜索/参考。

欢迎任何关于如何改进的反馈。

以下是一些示例订单和交货日期。

IF OBJECT_ID('tempdb..#Orders') IS NOT NULL DROP TABLE #Orders;  -- Example data to prove the theory.
    CREATE TABLE #Orders (OrderDate DateTime, DeliveryDate DATETIME)
    INSERT INTO #Orders  (#Orders.OrderDate, #Orders.DeliveryDate) 
    VALUES
       (cast('2020-01-18 13:55:15.000' as datetime), cast('2020-01-19 13:35:56.110' as datetime)), 
       (cast('2020-01-18 23:47:57.000' as datetime), cast('2020-01-19 13:36:40.537' as datetime)), 
       (cast('2020-01-18 07:20:12.000' as datetime), cast('2020-01-19 13:37:41.977' as datetime)), 
       (cast('2020-01-18 08:51:46.813' as datetime), cast('2020-01-19 13:38:35.193' as datetime)), 
       (cast('2020-01-18 12:37:13.000' as datetime), cast('2020-01-19 14:24:35.927' as datetime)), 
       (cast('2020-01-18 12:59:54.000' as datetime), cast('2020-01-19 14:53:23.663' as datetime)), 
       (cast('2020-01-19 13:44:31.000' as datetime), cast('2020-01-19 14:56:47.157' as datetime)), 
       (cast('2020-01-19 13:38:19.000' as datetime), cast('2020-01-19 14:58:09.543' as datetime)), 
       (cast('2020-01-19 08:55:31.050' as datetime), cast('2020-01-20 08:17:25.073' as datetime)), 
       (cast('2020-01-18 21:16:23.000' as datetime), cast('2020-01-20 08:17:52.330' as datetime)), 
       (cast('2020-01-19 08:59:26.650' as datetime), cast('2020-01-20 08:18:05.163' as datetime)), 
       (cast('2020-01-19 08:49:24.193' as datetime), cast('2020-01-20 08:18:49.077' as datetime)), 
       (cast('2020-01-18 15:33:48.000' as datetime), cast('2020-01-20 08:26:24.387' as datetime)), 
       (cast('2020-01-18 18:45:52.000' as datetime), cast('2020-01-20 08:26:29.657' as datetime)), 
       (cast('2020-01-18 20:56:33.000' as datetime), cast('2020-01-20 08:27:25.517' as datetime)), 
       (cast('2020-01-18 08:55:53.100' as datetime), cast('2020-01-20 08:28:25.210' as datetime)), 
       (cast('2020-01-06 00:19:08.000' as datetime), cast('2020-01-20 08:28:27.197' as datetime)), 
       (cast('2020-01-18 17:38:50.000' as datetime), cast('2020-01-20 08:42:16.777' as datetime)), 
       (cast('2020-01-19 14:24:30.000' as datetime), cast('2020-01-20 08:42:37.537' as datetime)), 
       (cast('2020-01-19 12:00:01.000' as datetime), cast('2020-01-20 08:42:53.173' as datetime)), 
       (cast('2020-01-19 13:21:15.000' as datetime), cast('2020-01-20 08:43:18.517' as datetime)), 
       (cast('2020-01-19 04:11:15.000' as datetime), cast('2020-01-20 09:28:34.997' as datetime)), 
       (cast('2020-01-19 09:28:05.000' as datetime), cast('2020-01-20 09:28:51.447' as datetime)), 
       (cast('2020-01-16 22:09:49.000' as datetime), cast('2020-01-20 09:29:23.630' as datetime)), 
       (cast('2020-01-19 13:43:05.000' as datetime), cast('2020-01-20 09:29:41.997' as datetime))

【问题讨论】:

    标签: sql tsql


    【解决方案1】:

    我已尽我所能对 SQL 进行了注释,以便用简单的英语解释我的方法。 还有一些虚拟数据可以实现这一点。

    下面是我的方法的粗略解释:

    使用 CTE - 为该查询可能引用的每一天创建一行数据。这可以在未来轻松扩展。

    然后查看从 CTE 返回的每一行,并根据星期几设置当天的工作小时数。还要设置当天的结束日期时间,再次取决于星期几。将其存储在 DayRows 表中。

    加入日期介于订单日期和交货日期之间的 DayRows 表,并总结这些日期可能的总工作时间。

    计算一天开始时间和下单时间之间的分钟差。计算交货日期和一天结束之间的分钟差。

    从总工作时间中减去这些差异。然后,这给出了两个日期之间的总工作分钟数。除以 60 以返回小时数。

    
    ---- calculates the number of working hours between order date and delivery date. Working day starts 08:00 each day. Weekdays ends at 17:30. Weekends ends at 16:00
    
    ; WITH TempDays AS 
        (SELECT CAST('2015-01-01 08:00:00' AS DATETIME) AS DateValue
        UNION ALL 
        SELECT DATEADD(DAY,1,DateValue) AS DateValue
        FROM TempDays
        WHERE 
           TempDays.DateValue <= '2035-12-31'
        ) -- Recursive CTE to give one row per day between 2015 and 2035
    
    
    , DayRows as
        ( 
           SELECT
              TempDays.DateValue AS StartDay
              , CASE    
                 WHEN datename(WEEKDAY,TempDays.DateValue) IN ('Saturday', 'Sunday')
                 THEN DATEADD(HOUR,8,TempDays.DateValue) -- Saturday & sunday
                 ELSE DATEADD(MINUTE,30,DATEADD(HOUR,9,TempDays.DateValue)) -- Weekday
                 END AS EndDay
              , CASE When datename(WEEKDAY,TempDays.DateValue) IN ('Saturday', 'Sunday')
                 Then 480 -- Saturday & Sunday 8 hours
                 ELSE 570 -- Weekday  9.5 hours
                 END AS WorkMinutes
           FROM 
              TempDays 
        ) -- This calcualtes the Start and End datetime for each day returned. If Weekend, end time is 16:00. Weekday is 17:30. All days start at 8:00
    
    
    
    SELECT
        #Orders.Orderdate
        , #Orders.DeliveryDate
        , WorkingHours.WorkMins
        , StartHours.MinutesDayStart
        , EndHours.MinutesDayEnd
        , CAST( ((WorkingHours.WorkMins - ISNULL(StartHours.MinutesDayStart,0) - ISNULL(EndHours.MinutesDayEnd,0))  / 60.00) AS DECIMAL(32,2)) AS Working_Hours_To_Ship -- Takes total working minutes for all the days inbetween order and delivery, then removes the number of mins between start of day and order and the delivery and end of the day.
    
    FROM 
        #Orders
        OUTER APPLY
           (
              SELECT
                 SUM(DayRows.WorkMinutes)  AS WorkMins 
              FROM 
                 DayRows
              WHERE 
                CAST(DayRows.StartDay AS DATE) >= CAST(#Orders.Orderdate AS DATE) AND CAST(DayRows.EndDay AS DATE) <= CAST(#Orders.DeliveryDate AS DATE) 
           ) AS WorkingHours -- Calculates the sum total of working hours for all days between the order date and delivery date, including the order and del date.
    
    
        OUTER APPLY
           (SELECT
              DATEDIFF(MINUTE,DayRows.StartDay,
                    CASE 
                        WHEN CAST(#Orders.Orderdate AS TIME) < cast(DayRows.StartDay AS TIME)
                        THEN DayRows.StartDay
                        ELSE 
                           CASE 
                              WHEN CAST(#Orders.Orderdate AS TIME) > cast(DayRows.EndDay AS TIME)
                              THEN DayRows.EndDay
                              Else #Orders.Orderdate
                              End
                        End
                    ) AS MinutesDayStart
           FROM 
              DayRows
           WHERE 
             CAST(DayRows.StartDay AS DATE) = CAST(#Orders.Orderdate AS DATE) 
           ) AS StartHours -- Calcualtes the number of minutes between the start of the day and the order date. This is then to be deducted off the total working hours.
    
        OUTER APPLY
           (SELECT
              DATEDIFF(MINUTE,
                 CASE 
                    WHEN CAST(#Orders.DeliveryDate AS TIME) < cast(DayRows.StartDay AS TIME) -- If the delivery was made before the start of the day, then uses start of day as delivery time.
                    THEN DayRows.StartDay
                    ELSE 
                        CASE 
                           WHEN CAST(#Orders.DeliveryDate AS TIME) > cast(DayRows.EndDay AS TIME)  -- If the delivery was made after the end of the day, then uses the end of the day as the delivery time
                           THEN DayRows.EndDay
                           Else #Orders.DeliveryDate
                        End
                 END,      
                 DayRows.EndDay
              )  AS MinutesDayEnd 
           FROM 
              DayRows
           WHERE 
             CAST(DayRows.StartDay AS DATE) = CAST(#Orders.DeliveryDate AS DATE) 
           ) AS EndHours -- Calculates the number of minutes between the delivery date and the end of the day.
    
    
    WHERE 
        #Orders.Orderdate >= '2020-01-01'
    
    option (maxrecursion 0)
    
    

    【讨论】:

    • 这是一个答案。我不能马上接受这个作为答案。
    猜你喜欢
    • 1970-01-01
    • 2017-01-05
    • 2017-10-09
    • 2016-01-17
    • 1970-01-01
    • 2019-04-15
    • 1970-01-01
    • 2011-04-17
    相关资源
    最近更新 更多