Сравнение диапазонов дат - PullRequest
109 голосов
/ 27 сентября 2008

В MySQL, если у меня есть список диапазонов дат (диапазон-начало и диапазон-конец). например,

10/06/1983 to 14/06/1983
15/07/1983 to 16/07/1983
18/07/1983 to 18/07/1983

И я хочу проверить, содержит ли другой диапазон дат ЛЮБОЙ из диапазонов, уже имеющихся в списке, как бы я это сделал?

, например

06/06/1983 to 18/06/1983 = IN LIST
10/06/1983 to 11/06/1983 = IN LIST
14/07/1983 to 14/07/1983 = NOT IN LIST

Ответы [ 10 ]

405 голосов
/ 27 сентября 2008

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

Позвольте привести пример.

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

           |-------------------|          compare to this one
               |---------|                contained within
           |----------|                   contained within, equal start
                   |-----------|          contained within, equal end
           |-------------------|          contained within, equal start+end
     |------------|                       not fully contained, overlaps start
                   |---------------|      not fully contained, overlaps end
     |-------------------------|          overlaps start, bigger
           |-----------------------|      overlaps end, bigger
     |------------------------------|     overlaps entire period

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

           |-------------------|          compare to this one
     |---|                                ends before
                                 |---|    starts after

Так что если вы просто уменьшите сравнение до:

starts after end
ends before start

тогда вы найдете все те, которые не перекрываются, и затем вы найдете все несовпадающие периоды.

В последнем примере NOT IN LIST вы можете видеть, что он соответствует этим двум правилам.

Вам нужно будет решить, находятся ли следующие периоды ВНУТРИ или ВНЕ ваших диапазонов:

           |-------------|
   |-------|                       equal end with start of comparison period
                         |-----|   equal start with end of comparison period

Если в вашей таблице есть столбцы с именами range_end и range_start, вот несколько простых SQL для извлечения всех соответствующих строк:

SELECT *
FROM periods
WHERE NOT (range_start > @check_period_end
           OR range_end < @check_period_start)

Обратите внимание на НЕ там. Так как два простых правила находят все несоответствующие строки, простое NOT обратит его к следующему: , если это не одна из несоответствующих строк, это должна быть одна из подходящих те .

Применяя здесь простую логику разворота, чтобы избавиться от НЕ, и в итоге вы получите:

SELECT *
FROM periods
WHERE range_start <= @check_period_end
      AND range_end >= @check_period_start
8 голосов
/ 27 сентября 2008

Принимая пример вашего диапазона от 06/06/1983 до 18/06/1983 и предполагая, что у вас есть столбцы с именами start и end для ваших диапазонов, вы можете использовать следующее предложение это

where ('1983-06-06' <= end) and ('1983-06-18' >= start)

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

4 голосов
/ 28 сентября 2008

Если ваша СУБД поддерживает функцию OVERLAP (), это становится тривиальным - нет необходимости в собственных решениях. (В Oracle это очевидно работает, но без документов).

0 голосов
/ 22 июня 2017
SELECT * 
FROM tabla a 
WHERE ( @Fini <= a.dFechaFin AND @Ffin >= a.dFechaIni )
  AND ( (@Fini >= a.dFechaIni AND @Ffin <= a.dFechaFin) OR (@Fini >= a.dFechaIni AND @Ffin >= a.dFechaFin) OR (a.dFechaIni>=@Fini AND a.dFechaFin <=@Ffin) OR
(a.dFechaIni>=@Fini AND a.dFechaFin >=@Ffin) )
0 голосов
/ 15 октября 2015

Другой метод с использованием оператора BETWEEN sql

Периоды включены:

SELECT *
FROM periods
WHERE @check_period_start BETWEEN range_start AND range_end
  AND @check_period_end BETWEEN range_start AND range_end

Периоды исключены:

SELECT *
FROM periods
WHERE (@check_period_start NOT BETWEEN range_start AND range_end
  OR @check_period_end NOT BETWEEN range_start AND range_end)
0 голосов
/ 07 января 2015

Попробуйте это на MS SQL


WITH date_range (calc_date) AS (
SELECT DATEADD(DAY, DATEDIFF(DAY, 0, [ending date]) - DATEDIFF(DAY, [start date], [ending date]), 0)
UNION ALL SELECT DATEADD(DAY, 1, calc_date)
FROM date_range 
WHERE DATEADD(DAY, 1, calc_date) <= [ending date])
SELECT  P.[fieldstartdate], P.[fieldenddate]
FROM date_range R JOIN [yourBaseTable] P on Convert(date, R.calc_date) BETWEEN convert(date, P.[fieldstartdate]) and convert(date, P.[fieldenddate]) 
GROUP BY  P.[fieldstartdate],  P.[fieldenddate];
0 голосов
/ 14 января 2013
CREATE FUNCTION overlap_date(s DATE, e DATE, a DATE, b DATE)
RETURNS BOOLEAN DETERMINISTIC
RETURN s BETWEEN a AND b or e BETWEEN a and b or  a BETWEEN s and e;
0 голосов
/ 19 декабря 2011

Посмотрите на следующий пример. Это будет полезно для вас.

    SELECT  DISTINCT RelatedTo,CAST(NotificationContent as nvarchar(max)) as NotificationContent,
                ID,
                Url,
                NotificationPrefix,
                NotificationDate
                FROM NotificationMaster as nfm
                inner join NotificationSettingsSubscriptionLog as nfl on nfm.NotificationDate between nfl.LastSubscribedDate and isnull(nfl.LastUnSubscribedDate,GETDATE())
  where ID not in(SELECT NotificationID from removednotificationsmaster where Userid=@userid) and  nfl.UserId = @userid and nfl.RelatedSettingColumn = RelatedTo
0 голосов
/ 03 мая 2011

Я создал функцию для решения этой проблемы в MySQL. Просто конвертируйте даты в секунды перед использованием.

DELIMITER ;;

CREATE FUNCTION overlap_interval(x INT,y INT,a INT,b INT)
RETURNS INTEGER DETERMINISTIC
BEGIN
DECLARE
    overlap_amount INTEGER;
    IF (((x <= a) AND (a < y)) OR ((x < b) AND (b <= y)) OR (a < x AND y < b)) THEN
        IF (x < a) THEN
            IF (y < b) THEN
                SET overlap_amount = y - a;
            ELSE
                SET overlap_amount = b - a;
            END IF;
        ELSE
            IF (y < b) THEN
                SET overlap_amount = y - x;
            ELSE
                SET overlap_amount = b - x;
            END IF;
        END IF;
    ELSE
        SET overlap_amount = 0;
    END IF;
    RETURN overlap_amount;
END ;;

DELIMITER ;
0 голосов
/ 29 сентября 2008

В ваших ожидаемых результатах вы говорите

06/06 / 1983–18/06/1983 = В СПИСОК

Однако этот период не содержит и не содержится ни в одном из периодов в вашей таблице (не в списке!). Однако он перекрывает период с 10 июня 1983 года по 14 июня 1983 года.

Вы можете найти книгу Snodgrass (http://www.cs.arizona.edu/people/rts/tdbbook.pdf) полезной: она предшествует MySQL, но концепция времени не изменилась; -)

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