简体   繁体   English

SQL Server 2008 - 考虑到自定义假日和周末,将两个日期之间的业务分钟相加

[英]SQL Server 2008 - Sum business minutes between two dates taking into account custom holidays and weekends

Using SQL Server 2008, I need to sum the business minutes between two datetime fields, while taking into consideration non working hours and weekends/company holidays. 使用SQL Server 2008,我需要将两个日期时间字段之间的业务分钟相加,同时考虑非工作时间和周末/公司假期。 I would like to incorporate a calendar if possible so that should I need to edit any holiday it could be easily done. 如果可能的话,我想合并一个日历,这样我就可以编辑任何可以轻松完成的假期。

Eg 例如

OpenCall              CloseCall 
05/08/2013 14:00:00   06/08/2013 09:30:00             

The result for the above, needs to return: 240 -- (4 hours) working hours are: 08:30-17:00. 结果如上,需要返回:240 - (4小时)工作时间为:08:30-17:00。

If the call was open on Friday and was closed on Tuesday, then it should only calculate the minutes between the working hours on Friday, Monday and Tuesday (ie not the weekend). 如果电话在星期五开放并在星期二关闭,那么它应该只计算星期五,星期一和星期二(即不是周末)的工作时间之间的分钟数。

I'm new to SQL/T-SQL so please explain any code/variables clearly - IF you can find a neat solution! 我是SQL / T-SQL的新手,所以请清楚地解释任何代码/变量 - 如果你能找到一个简洁的解决方案!

Thanks in advance! 提前致谢!

First, This is the structure I have used, I don't think it will take much adapting to fit it into your structure. 首先,这是我使用的结构,我认为它不会适应你的结构。

(Note I would recommend a lot more fields in your calendar table, but IsWorkingDay is the only one required for this example) (注意我会在你的日历表中推荐更多字段,但IsWorkingDay是这个例子中唯一需要的字段)

SET DATEFIRST 1;
CREATE TABLE dbo.Calendar
(       [Date]          DATE NOT NULL,
        IsWorkingDay    BIT NOT NULL
    CONSTRAINT PK_Calendar_Date PRIMARY KEY ([Date])
);

-- INSERT DATES IN 2013 (NOT DOING A FULL TABLE AS IT'S JUST AN EXAMPLE)
INSERT dbo.Calendar ([Date], IsWorkingDay)
SELECT  [Date] = DATEADD(DAY, Number, '20130101'), 1
FROM    Master..spt_values
WHERE   Type = 'P'
AND     Number < 365;

-- UPDATE NON WORKING DAYS
UPDATE  dbo.Calendar
SET     IsWorkingDay = 0
WHERE   DATEPART(WEEKDAY, [Date]) IN (6, 7)
OR      [Date] IN ('20130101', '20130329', '20130401', '20130506', '20130527', '20130826', '20131225', '20131226');

-- CREATE SAMPLE DATA
CREATE TABLE T (OpenCall DATETIME NOT NULL, CloseCall DATETIME NOT NULL);
INSERT T (OpenCall, CloseCall)
VALUES 
    ('20130805 14:00:00', '20130806 09:30:00'),
    ('20130823 16:00:00', '20130828 10:30:00'); -- CROSS BANK HOLIDAY AND WEEKEND

The first step is to get all days between your two dates. 第一步是在两个日期之间获取所有日期。 You can do this by joining to the calendar table where the date in the calender table is between the opening and closing datetimes: 您可以通过加入日历表来执行此操作,其中日历表中的日期位于开始和结束日期时间之间:

SELECT  T.OpenCall,
        T.CloseCall,
        Calendar.[Date],
        StartTime = CASE WHEN CAST(T.OpenCall AS DATE) = Calendar.[Date] THEN CAST(T.OpenCall AS TIME) ELSE CAST('08:30' AS TIME) END,
        EndTime = CASE WHEN CAST(T.CloseCall AS DATE) = Calendar.[Date] THEN CAST(T.CloseCall AS TIME) ELSE CAST('17:00' AS TIME) END
FROM    T
        INNER JOIN Calendar
            ON Calendar.Date >= CAST(T.OpenCall AS DATE)
            AND Calendar.Date <= CAST(T.CloseCall AS DATE)
            AND Calendar.IsWorkingDay = 1;

For the example data, this would give 对于示例数据,这将给出

+---------------------+---------------------+------------+----------+----------+
| OpenCall            | CloseCall           |   Date     |StartTime | EndTime  |
|---------------------+---------------------+------------+----------+----------|
| 2013-08-05 14:00:00 | 2013-08-06 09:30:00 | 2013-08-05 | 14:00:00 | 17:00:00 |
| 2013-08-05 14:00:00 | 2013-08-06 09:30:00 | 2013-08-06 | 08:30:00 | 09:30:00 |
|---------------------+---------------------+------------+----------+----------|
| 2013-08-23 16:00:00 | 2013-08-28 10:30:00 | 2013-08-23 | 16:00:00 | 17:00:00 |
| 2013-08-23 16:00:00 | 2013-08-28 10:30:00 | 2013-08-27 | 08:30:00 | 17:00:00 |
| 2013-08-23 16:00:00 | 2013-08-28 10:30:00 | 2013-08-28 | 08:30:00 | 09:30:00 |
+---------------------+---------------------+------------+----------+----------+

As you can see, on the first day it uses the open time from the source data , and on the last day of each range it uses the close time from source data, for all other start/end times it uses hard coded business hours (in this case 9am-5.30pm). 如您所见,第一天它使用源数据的开放时间,并且在每个范围的最后一天使用源数据的关闭时间,对于所有其他开始/结束时间,它使用硬编码的营业时间(在这种情况下,上午9点至下午5点30分)。

The last step would just be to sum up the difference between the starttime and the endtime for each range: 最后一步只是总结每个范围的开始时间和结束时间之间的差异:

WITH Data AS
(   SELECT  T.OpenCall,
            T.CloseCall,
            StartTime = CASE WHEN CAST(T.OpenCall AS DATE) = Calendar.[Date] THEN CAST(T.OpenCall AS TIME) ELSE CAST('08:30' AS TIME) END,
            EndTime = CASE WHEN CAST(T.CloseCall AS DATE) = Calendar.[Date] THEN CAST(T.CloseCall AS TIME) ELSE CAST('17:00' AS TIME) END
    FROM    T
            INNER JOIN Calendar
                ON Calendar.Date >= CAST(T.OpenCall AS DATE)
                AND Calendar.Date <= CAST(T.CloseCall AS DATE)
                AND Calendar.IsWorkingDay = 1
)
SELECT  OpenCall,
        CloseCall,
        BusinessMinutes = SUM(DATEDIFF(MINUTE, StartTime, EndTime))
FROM    Data
GROUP BY OpenCall, CloseCall;

Giving an end result of: 给出最终结果:

+---------------------+---------------------+--------------------+
| OpenCall            | CloseCall           |   BusinessMinutes  |
|---------------------+---------------------+--------------------+
| 2013-08-05 14:00:00 | 2013-08-06 09:30:00 |        240         |
| 2013-08-23 16:00:00 | 2013-08-28 10:30:00 |        690         |
+---------------------+---------------------+--------------------+

Example on SQL Fiddle 关于SQL小提琴的例子

Here's my attempt. 这是我的尝试。 The goal was to get this query without table of dates for each date in period. 目标是获取此查询而不包含期间中每个日期的日期表。 I think this could work faster for long periods, but have not tested it. 我认为这可以长时间更快地工作,但还没有测试过。

declare @Start_Time time = '08:30', @End_Time time = '17:00'
declare @Whole_Date_Minutes int = datediff(mi, @Start_Time, @End_Time)

;with cte as (
    select
        C.OpenCall, C.CloseCall,
        cast(C.OpenCall as date) as OpenCallDate,
        case when cast(C.OpenCall as time) < @Start_Time then @Start_Time else cast(C.OpenCall as time) end as OpenCallTime,
        cast(C.CloseCall as date) as CloseCallDate,
        case when cast(C.CloseCall as time) > @End_Time then @End_Time else cast(C.CloseCall as time) end as CloseCallTime
    from @Calls as C
), cte2 as (
    select
        OpenCall, CloseCall, OpenCallDate, OpenCallTime,
        case when CloseCallDate > OpenCallDate then OpenCallDate else CloseCallDate end as CloseCallDate,
        case when CloseCallDate > OpenCallDate then @End_Time else CloseCallTime end as CloseCallTime
    from cte
    union all
    select
        OpenCall, CloseCall, dateadd(dd, 1, OpenCallDate) as OpenCallDate, @Start_Time as OpenCallTime,
        CloseCallDate, CloseCallTime
    from cte
    where CloseCallDate > OpenCallDate
)
select
    c.OpenCall, c.CloseCall,
    sum(
        @Whole_Date_Minutes + 
        datediff(dd, c.OpenCallDate, CloseCallDate) * @Whole_Date_Minutes - 
        datediff(mi, @Start_Time, c.OpenCallTime) - 
        datediff(mi, c.CloseCallTime, @End_Time) -
        H.[Days] * @Whole_Date_Minutes
    ) as BusinessMinutes 
from cte2 as c
    outer apply (select count(*) as [Days] from @Holidays as H where H.[Date] >= c.OpenCallDate and H.[Date] <= c.CloseCallDate) as H
group by c.OpenCall, c.CloseCall

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 没有周末和假期的两个日期之间的差异Sql查询ORACLE - difference between two dates without weekends and holidays Sql query ORACLE SQL Server 查询以获取两个日期之间的工作日数,不包括假期 - SQL Server query to get the number of business days between 2 dates, excluding holidays 计算两个日期之间的 SUM SQL SERVER - Calculate SUM between two dates SQL SERVER SQL Server 2008:如何选择两个连续会话之间的差小于10分钟的所有会话的总和 - SQL Server 2008 : how to select sum of all sessions where difference between two consecutive sessions is less than 10 minutes SQL Server 2008中的小时和分钟总和 - Sum of hour and minutes in SQL Server 2008 两个日期时间之间的SQL业务分钟 - SQL business minutes between two datetimes 初学者计算日期之间的天数,不包括周末和假日 - Beginner way to count days between dates excluding weekends and holidays 查找两个日期之间的中位数SQL Server 2008 - Finding Median between TWO dates SQL Server 2008 如何计算两个日期之间的营业时间/分钟? - How to calculate business hours/minutes between two dates? 如何在工作日 SQL Server 的工作时间内计算两个日期之间的差异(以秒为单位)? - How to calculate difference between two dates in seconds during business hours for business days SQL Server?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM