日期间的另一个SQL时间减去周末问题

时间:2018-09-25 20:45:46

标签: sql sql-server

我有两个约会:CREATION_DATESTART_DATESTART_DATE将始终晚于CREATION_DATE。我需要计算它们之间的分钟数,除了周末发生的分钟数。

我能找到的每个解决方案都假定其中一个日期发生发生在周末,但是可惜的是,如果CREATION_DATE是在星期五,而START_DATE是星期一,则所有周六和周日的计数。

我什至尝试计算从CREATION_DATE到下一个12am的分钟,发生时间是从星期一的第一个12am到START_DATE plus 分钟,但这都不起作用。

如果我只想数几天,我已经找到了解决方案。我需要知道几分钟。

我们的数据库是托管的,我无法创建VB函数,因此我的解决方案必须全部为SQL。

3 个答案:

答案 0 :(得分:1)

基本思路是在开始和结束之间的所有分钟分钟内生成记录,包括周末的记录。然后使用WHERE子句过滤掉不需要的内容。在许多情况下,这是通过连接到Calendar表来完成的,因此您也可以查看假期或其他特殊事件,但是为此,我们可以仅使用DATEPART()函数。

完成此操作后,我们使用GROUP BY将内容回滚到原始日期值,并使用COUNT()函数知道我们做了多少工作。

无论您是数天,数分钟,数月还是其他时间,此基本概念都有效。

这个问题尚不清楚,但我将假设您的开始和结束值是表中的列,而不是变量名(无@)。

WITH Numbers(Number) AS
(
    SELECT ROW_NUMBER() OVER (ORDER BY s1.[object_id]) - 1
    FROM sys.all_columns AS s1
    CROSS JOIN sys.all_columns AS s2
)
SELECT t.CREATION_DATE, t.START_DATE, COUNT(*) AS Num_Minutes
FROM [MyTable] t
INNER JOIN Numbers n on n.Number <= DATEDIFF(minute, t.CREATION_DATE, t.START_DATE)
WHERE DATEPART(dw, DATEADD(minute, n.Number, t.CREATION_DATE)) NOT IN (7,1)
GROUP BY t.CREATION_DATE, t.START_DATE

但这可能会非常缓慢,具体取决于日期之间的距离。您可以通过使用其他各种方式来生成Numbers表来获得一个更好地逼近实际数据需求的起点,从而对此进行改进。

答案 1 :(得分:0)

如果您不担心为假期做会计核算,则可以将其作为一个简单的数学问题来进行,而不必费力地查看理货表或进行任何计数。 下面的工作方式是从开始日期和结束日期参数中删除时间部分,并从中计算工作日的天数,然后将其乘以该数字的3660。从那里开始,如果开始日期是工作日,则减去开始日期分钟。 。如果结束日期是工作日,则会添加这些分钟。

DECLARE
    @BegDate DATETIME = '2018-09-13 03:30:30',
    @EndDate DATETIME = '2018-09-18 03:35:27';

SELECT 
    working_mins = bm.base_mins
                    - ((1 - (x.is_beg_sat + x.is_beg_sun)) * x.beg_mins)    -- if the begin date is a week day, subtract the mins from midnight.
                    + ((1 - (x.is_end_sat + x.is_end_sun)) * x.end_mins)    -- if the end date is a week day add the mins from midnight.
    --,*
FROM
    ( VALUES (
            DATEADD(DAY, DATEDIFF(DAY, 0, @BegDate), 0), 
            DATEADD(DAY, DATEDIFF(DAY, 0, @EndDate), 0)
            ) ) d (beg_date, end_date)
    CROSS APPLY ( VALUES (
            DATEDIFF(MINUTE, d.beg_date, @BegDate),
            DATEDIFF(MINUTE, d.end_date, @EndDate),
            DATEDIFF(DAY, d.beg_date, d.end_date),
            DATEDIFF(WEEK, d.beg_date, d.end_date) * 2,
            1 - SIGN(DATEPART(WEEKDAY, d.beg_date) % 7),
            1 - SIGN(DATEPART(WEEKDAY, d.end_date) % 7),
            1 - SIGN((DATEPART(WEEKDAY, d.beg_date) + 7) % 8),
            1 - SIGN((DATEPART(WEEKDAY, d.end_date) + 7) % 8)
            ) ) x (beg_mins, end_mins, total_days, weekend_days, is_beg_sat, is_end_sat, is_beg_sun, is_end_sun)
    CROSS APPLY ( VALUES (1440 * (x.total_days - x.weekend_days + x.is_beg_sat - x.is_end_sat)) ) bm (base_mins);

答案 2 :(得分:0)

您可以看一下该解决方案,看看是否满足您的需求。基本上,我做了以下事情:

  1. StartDateEndDate之间不是周末的整天数乘以2:

    SELECT COUNT(*) * 24 * 60 FROM WholeDaysBetween WHERE wkday <= 5

  2. StartDatehours*60 + minutes)开始记录

    (24 * 60) - (DATEPART(HOUR, @StartDate) * 60) - (DATEPART(MINUTE, @StartDate))

  3. EndDatehours*60 + minutes)开始记录

    (DATEPART(HOUR, @EndDate) * 60) + (DATEPART(MINUTE, @EndDate))

为了获得两次之间的整天数,我使用了递归CTE:

WITH
    WholeDaysBetween(dt, wkday) AS
    (
        SELECT DATEADD(DAY, 1, @StartDate), DATEPART(WEEKDAY, DATEADD(DAY, 1, @StartDate))
        UNION ALL
        SELECT DATEADD(DAY, 1, dt), DATEPART(WEEKDAY, DATEADD(DAY, 1, dt))
        FROM WholeDaysBetween
        WHERE dt < DATEADD(DAY, -1, @EndDate)
    )

当然,要使其正常工作,您必须adjust your datefirst settings

最终查询如下(我使用了与您的评论相同的示例数据):

set datefirst 1; -- day starts on Monday

declare @StartDate datetime = '2018-09-21 23:59:00';
declare @EndDate datetime = '2018-09-24 00:01:00';

WITH
    WholeDaysBetween(dt, wkday) AS
    (
        SELECT DATEADD(DAY, 1, @StartDate), DATEPART(WEEKDAY, DATEADD(DAY, 1, @StartDate))
        UNION ALL
        SELECT DATEADD(DAY, 1, dt), DATEPART(WEEKDAY, DATEADD(DAY, 1, dt))
        FROM WholeDaysBetween
        WHERE dt < DATEADD(DAY, -1, @EndDate)
    )

SELECT
    -- whole weekdays between @StartDate and @EndDate, 
    -- multiplied by minutes per day
    (
        SELECT COUNT(*) * 24 * 60
        FROM WholeDaysBetween
        WHERE wkday <= 5
    ) 
    +
    -- minutes from @StartDate date to end of @StartDate
    -- as long as @StartDate isn't on weekend
    (
        SELECT 
            CASE 
                WHEN DATEPART(WEEKDAY, @StartDate) <= 5
                THEN
                (24 * 60) - 
                (DATEPART(HOUR, @StartDate) * 60) - 
                (DATEPART(MINUTE, @StartDate))
                ELSE 0
            END
    )
    +
    -- minutes from start of @EndDate's date to @EndDate
    -- as long as @EndDate isn't on weekend
    (
        SELECT 
            CASE
            WHEN DATEPART(WEEKDAY, @EndDate) <= 5
            THEN
                (DATEPART(HOUR, @EndDate) * 60) +
                (DATEPART(MINUTE, @EndDate))
            ELSE 0
            END
    )