Как проверить, чтобы даты не перекрывались в таблице с использованием TSQL - PullRequest
4 голосов
/ 27 мая 2010

У меня есть таблица с датами начала и окончания, которые мне нужно определить, если какие-либо совпадения, и не совсем уверен, лучший путь.

Первоначально я думал об использовании вложенного курсора, как показано ниже, который работает, однако я проверяю одни и те же записи друг против друга дважды, и я уверен, что это не очень эффективно.

Например: эта таблица приведет к перекрытию.

id  start                       end
-------------------------------------------------------
1   2009-10-22 10:19:00.000     2009-10-22 11:40:00.000
2   2009-10-22 10:31:00.000     2009-10-22 13:34:00.000
3   2009-10-22 16:31:00.000     2009-10-22 17:34:00.000

Declare @Start datetime, @End datetime, @OtherStart datetime, @OtherEnd datetime, @id int, @endCheck bit

Set @endCheck = 0

DECLARE Cur1 CURSOR FOR
        select id, start, end from table1 

OPEN Cur1
FETCH NEXT FROM Cur1 INTO @id, @Start, @End
WHILE @@FETCH_STATUS = 0 AND @endCheck = 0
BEGIN
    -- Get a cursor on all the other records
    DECLARE Cur2 CURSOR FOR
            select start, end from table1 
                and id != @id AND start < @end
    OPEN Cur2
    FETCH NEXT FROM Cur2 INTO @OtherStart, @OtherEnd
    WHILE @@FETCH_STATUS = 0 AND @endCheck = 0
    BEGIN

            if ( @Start > @OtherStart AND @Start < @OtherEnd    OR
                 @End > @OtherStart AND @End < @OtherEnd )
                or
               ( @OtherStart > @Start AND @OtherStart < @End    OR
                 @OtherEnd > @Start AND @OtherEnd < @End )

            BEGIN
                SET @endCheck = 1
            END

            FETCH NEXT FROM Cur2 INTO @OtherStart, @OtherEnd
    END
    CLOSE Cur2
    DEALLOCATE Cur2

    FETCH NEXT FROM Cur1 INTO @id, @Start, @End
END
CLOSE Cur1
DEALLOCATE Cur1

Ответы [ 4 ]

8 голосов
/ 27 мая 2010
  • Во-первых, давайте подробно рассмотрим, что означает «перекрытие», и оптимизируем ваше логическое выражение.

Перекрытие - это любое из следующих условий:

                 Start1                     End1
---------------------------------------------------------------------------------
 Start2                       End2
 Start2                                                       End2
                             Start2                           End2

И НЕ является одним из следующих условий:

                 Start1                     End1
---------------------------------------------------------------------------------
 Start2  End2
                                                      Start2  End2

Если вы посмотрите внимательно, вы заметите, Start2 < End1 AND End2 > Start1 определяет это отношение, так что вы можете уменьшить свое выражение до этого.


  • Во-вторых, вы можете поместить это в WHERE состояние для вашего курсора вместо того, чтобы проходить по каждой строке и проверять.

  • В-третьих, поскольку вы просто проверяете, существует ли что-то или нет, вы можете использовать предложение IF EXISTS вместо того, чтобы проходить курсором и проверять.

  • Наконец, вы можете сжать все это до одного запроса, используя INNER JOIN для себя или WHERE EXISTS, в зависимости от того, как вы хотите, чтобы ваш конечный результат выглядел. Чтобы получить все перекрытия в таблице, вы можете попробовать что-то вроде следующего (t2.id > t1.id - получить каждое перекрытие только один раз):

    SELECT t1.id, t2.id
    FROM MyTable t1
    INNER JOIN MyTable t2 ON t2.id > t1.id 
                          AND t2.start < t1.end AND t2.end > t1.start
    
3 голосов
/ 27 мая 2010

Я не уверен, как бы вы хотели, чтобы получаемый результат выглядел, но вы можете попробовать это:

SELECT 
    TD1.* 
FROM 
    table1 TD1 
    JOIN table1 TD2 ON 
        TD1.start BETWEEN TD2.start AND TD2.[end] 
        AND TD1.id != TD2.id
1 голос
/ 27 мая 2010

Я думаю, все, что вам нужно сделать, это сравнить дату окончания с следующей датой начала, чтобы выяснить, что перекрывается. Это может вернуть дубликаты, и вы можете добавить DISTINCT, если вам нужна только 1 копия каждого перекрытия.

SELECT a.* FROM Table1 a
INNER JOIN Table1 b
ON a.End > b.Start AND a.id <> b.id
1 голос
/ 27 мая 2010

Вы можете попробовать этот запрос. По сути, он выбирает идентификатор из таблицы 1, если время начала наступает между временем начала и окончания любой другой записи.

Это даст вам все записи, которые «запустили пистолет» и начались до того, как закончилась другая запись.

select id
from table1 f1
where exists(
    select 1
    from table1 f2
    where f1.id <> f2.id
        AND f1.start >= f2.start
        AND f1.start <= f2.[end]
)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...