SQL - 检查两个时间跨度以进行重叠

时间:2018-03-16 14:25:56

标签: sql sql-server time

我一直致力于检测SQL Server中重叠时间跨度的解决方案。这是为了防止事件重叠,或者在我的情况下,工作轮班。

虽然对finding overlapping date ranges的SO进行了大量讨论,但重叠时间跨度的重要性较少,而当时间跨度允许跨越午夜时则更少。通过大量的研究和测试,我提出了以下解决方案。

CREATE TABLE Shift
(
    ShiftID INT IDENTITY(1,1),
    StartTime TIME(0) NOT NULL,
    EndTime TIME(0) NOT NULL
);

CREATE PROCEDURE [dbo].[spInsertShift]
    @StartTime                      TIME(0),
    @EndTime                        TIME(0)
AS
BEGIN

    DECLARE @ThrowMessage           NVARCHAR(4000)

    -- Check whether the new shift would overlap with any existing shifts.
    IF EXISTS
    (
        SELECT 0
        FROM Shift
        WHERE 
        (
            -- Case #1: Neither shift crosses midnight.
            (@StartTime < @EndTime AND StartTime < EndTime)

            -- New shift would overlap with an existing shift.
            AND @StartTime < EndTime
            AND StartTime < @EndTime
        )
        OR
        (
            -- Case #2: Both shifts cross midnight.
            (@EndTime < @StartTime AND EndTime < StartTime)

            -- New shift would overlap with an existing shift.
            AND CAST(@StartTime AS DATETIME) < DATEADD(DAY, 1, CAST(EndTime AS DATETIME))
            AND CAST(StartTime AS DATETIME) < DATEADD(DAY, 1, CAST(@EndTime AS DATETIME))
        )
        OR
        (
            -- Case #3: New shift crosses midnight, but the existing shift does not.
            (@EndTime < @StartTime AND StartTime < EndTime)
            AND
            (
                -- New shift would overlap with an existing shift.
                @StartTime > StartTime AND @StartTime < EndTime
                OR @EndTime > StartTime AND @EndTime < EndTime
                OR
                (
                    -- Existing shift would be inside new shift.
                    CAST(StartTime AS DATETIME) BETWEEN CAST(@StartTime AS DATETIME) AND DATEADD(DAY, 1, CAST(@EndTime AS DATETIME))
                    AND CAST(EndTime AS DATETIME) BETWEEN CAST(@StartTime AS DATETIME) AND DATEADD(DAY, 1, CAST(@EndTime AS DATETIME))
                )
            )
        )
        OR
        (
            -- Case #4: New shift does not cross midnight, but the existing shift does.
            (@StartTime < @EndTime AND EndTime < StartTime)
            AND
            (
                -- Existing shift would overlap with new shift.
                StartTime > @StartTime AND StartTime < @EndTime
                OR EndTime > @StartTime AND EndTime < @EndTime
                OR
                (
                    -- New shift would be inside an existing shift.
                    CAST(@StartTime AS DATETIME) BETWEEN CAST(StartTime AS DATETIME) AND DATEADD(DAY, 1, CAST(EndTime AS DATETIME))
                    AND CAST(@EndTime AS DATETIME) BETWEEN CAST(StartTime AS DATETIME) AND DATEADD(DAY, 1, CAST(EndTime AS DATETIME))
                )
            )
        )
    )
    BEGIN
        SET @ThrowMessage = 'A Shift already exists in this timespan.'
        THROW 50140, @ThrowMessage, 1
    END

    INSERT INTO Shift
    (
        StartTime,
        EndTime
    )
    VALUES
    (
        @StartTime,
        @EndTime
    )

END

[SQL Fiddle]

由于我的时间跨度允许跨越午夜,我不得不使用某种机制来表示EndTime大于StartTime,尽管事实上StartTime='22:00' > EndTime='06:00'。在这些情况下,我选择CAST EndTime为DATETIME并添加一天。

我的问题是:检测允许跨越午夜的重叠时间跨度的最佳方法是什么?我目前的解决方案感觉过于冗长和复杂。测试时,请记住所有timespan test cases。谢谢!

2 个答案:

答案 0 :(得分:0)

您应该考虑在表格中添加日期字段,然后尝试处理您的数据。通过这种方式,您可以轻松找到轮班的确切时间段。

答案 1 :(得分:0)

似乎使用DATETIME而不是TIME数据类型可以完美地解决您的问题。

通过获取日期和时间信息的值,可以捕获跨越午夜的跨度,并且可以与琐碎的努力进行比较。