Рассчитать пропущенные диапазоны дат и перекрывающиеся диапазоны дат между двумя датами - PullRequest
6 голосов
/ 22 июня 2009

У меня есть следующий набор дат (дд / мм / гггг) соответствующих событий в моей базе данных:

eventId     startDate     endDate
1           02/05/2009    10/05/2009
2           08/05/2009    12/05/2009
3           10/05/2009    12/05/2009
4           21/05/2009    21/05/2009
5           25/05/2009    NULL
6           01/06/2009    03/06/2009

События имеют дату начала и окончания (время не имеет значения), а значение NULL endDate означает, что событие все еще продолжается.

То, что я хотел бы определить, - это диапазоны дат между двумя произвольными датами, когда a) не было события и b) события перекрывались.

Таким образом, для диапазона дат ввода 01/04/2009 - 30/06/2009 я ожидал бы получить следующие результаты:

no event: 01/04/2009 - 01/05/2009
overlap : 08/05/2009 - 10/05/2009
overlap : 10/05/2009 - 12/05/2009
no event: 13/05/2009 - 20/05/2009
no event: 22/05/2009 - 24/05/2009
overlap : 01/06/2009 - 03/06/2009

Обратите внимание, что два соседних диапазона перекрытия будут приемлемы как один результат.

Может кто-нибудь помочь мне с алгоритмом SQL для генерации этого набора результатов?

РЕДАКТИРОВАТЬ: целевой платформой базы данных является SQL Server 2005. Даты записываются как 10/05/2009 00:00:00, что означает, что событие закончилось некоторое время между 5/5/2009 00:00:00 и 10 / 5/2009 23:59:59. То же самое верно для дат начала. Поэтому диапазон вводимых дат также можно прочитать как 01/04/2009 00:00:00 - 30/06/2009 23: 59: 59.

Ответы [ 3 ]

4 голосов
/ 22 июня 2009

Это небольшая вариация функции сглаживания пересекающихся интервалов времени в SQL Server:

Это один из редких случаев, когда основанный на курсоре подход в SQL Server быстрее основанного на наборе подходов:


CREATE FUNCTION mytable(@p_from DATETIME, @p_till DATETIME)
RETURNS @t TABLE
        (
        q_type VARCHAR(20) NOT NULL,
        q_start DATETIME NOT NULL,
        q_end DATETIME NOT NULL
        )
AS
BEGIN
        DECLARE @qs DATETIME
        DECLARE @qe DATETIME
        DECLARE @ms DATETIME
        DECLARE @me DATETIME
        DECLARE cr_span CURSOR FAST_FORWARD
        FOR
        SELECT  startDate, endDate
        FROM    mytable
        WHERE   startDate BETWEEN @p_from AND @p_till
        ORDER BY
                startDate 
        OPEN    cr_span
        FETCH   NEXT
        FROM    cr_span
        INTO    @qs, @qe
        SET @ms = @qs
        SET @me = @qe
        WHILE @@FETCH_STATUS = 0
        BEGIN
                FETCH   NEXT
                FROM    cr_span
                INTO    @qs, @qe
                IF @qs > @me
                BEGIN
                        INSERT
                        INTO    @t
                        VALUES ('overlap', @ms, @me)
                        INSERT
                        INTO    @t
                        VALUES ('gap', @me, @qs)
                        SET @ms = @qs
                END
                SET @me = CASE WHEN @qe > @me THEN @qe ELSE @me END
        END
        IF @ms IS NOT NULL
        BEGIN
                INSERT
                INTO    @t
                VALUES  (@ms, @me)
        END
        CLOSE   cr_span
        RETURN
END
GO

Эта функция сжимает каждый смежный набор пересекающихся диапазонов водин диапазон и возвращает диапазон и следующий пробел.

1 голос
/ 22 июня 2009

Не понимая, какую проблему вы пытаетесь решить, вот мое решение какой-то проблемы с моей головы:

  1. Создать табличную функцию (UDF) "все даты", которая будет возвращать все даты в году.
  2. Конвертируйте ваши события в отдельные даты (одна строка событий станет столько строк, сколько в ней дней), объединяя события внутри всех дат where the date is between event's start and end dates ... Сохранить исходный идентификатор события.
  3. Выполните внешнее соединение дат событий со всеми датами (снова), чтобы найти пропуски или пропуски.
  4. Присоединяйтесь к датам событий на where dates are same but eventId is not, чтобы найти совпадения.
0 голосов
/ 10 сентября 2009

мой бедный, с postgresql, вы можете сделать это просто:

(начало1, конец1) ПЕРЕКРЫТИЯ (начало2, конец2) (начало1, длина1) ПЕРЕКРЫТИЯ (начало2, длина2)

Это выражение дает истину, когда два периода времени (определенные их конечными точками) перекрываются, ложь, когда они не перекрываются. Конечные точки могут быть указаны в виде пар дат, времени или отметок времени; или как дата, время или отметка времени с последующим интервалом.

ВЫБРАТЬ (ДАТА '2001-02-16', ДАТА '2001-12-21') ПЕРЕКРЫТИЯ (ДАТА '2001-10-30', ДАТА '2002-10-30'); Результат: правда ВЫБЕРИТЕ (ДАТА '2001-02-16', ИНТЕРВАЛ '100 дней') ПЕРЕКРЫТИЯ (ДАТА '2001-10-30', ДАТА '2002-10-30'); Результат: ложь

но под сервером sql я не знаю ... извините

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...