В 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


Это классическая проблема, и ее на самом деле легче, если вы перевернете логику.
Позвольте привести пример.
Я размещу здесь один период времени и все различные вариации других периодов, которые так или иначе пересекаются.
|-------------------| 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
тогда вы найдете все те, которые не пересекаются, а затем вы найдете все несовпадающие периоды.
В вашем последнем примере «НЕ В СПИСКЕ» вы можете видеть, что он соответствует этим двум правилам.
Вам нужно будет решить, находятся ли следующие периоды В или ВНЕ ваших диапазонов:
|-------------|
|-------| 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)
Обратите внимание на НЕТ там. Поскольку два простых правила находят все строки несоответствие, простое НЕ изменит его, чтобы сказать: если это не одна из несовпадающих строк, она должна быть одной из совпадающих.
Применяя здесь простую логику разворота, чтобы избавиться от НЕ, вы получите:
SELECT *
FROM periods
WHERE range_start <= @check_period_end
AND range_end >= @check_period_start
Нам нужен флаг "содержит диаграммы ACII" для ответов, который позволит вам проголосовать за них более одного раза.
Вероятно, один из 5 лучших ответов, которые я видел на SO. Отличное объяснение проблемы, хорошее пошаговое руководство по решению и ... фотографии!
Да, это было абсолютно великолепно, спасибо, абсолютно неправильно смотрел на это и боролся с датами в моей голове. Только что написал свои запросы, используя обратное, и теперь это очень ясно!
Супер ответ! Абсолютно люблю то, как это было ясно объяснено.
Если бы я мог проголосовать за это более одного раза, я бы это сделал. Великолепное, четкое и краткое объяснение часто возникающей проблемы, решение, которое я редко видел так хорошо объясненным!
Отличный ответ! Единственное, что я бы добавил - в отношении решения, включены ли конечные точки или нет - все работает чище, если вы используете закрытый интервал с одной стороны и открытый интервал с другой. Например. начало диапазона входит в точку, а конец диапазона - нет. Все становится проще, особенно когда вы имеете дело с комбинацией дат и времени различного разрешения.
Хороший ответ. Это также описывается как Алгебра интервалов Аллена. У меня похожий отвечать, и я вступил в ожесточенную битву за то, сколько разных сравнений есть с одним комментатором.
Я ломал себе голову из-за подобной проблемы, и меня поразило, как я не увидел такого подхода. Большое спасибо за эту блестящую точку зрения, и искусство ASCII
Единственное, что я бы добавил, так это то, что для того, чтобы это сработало, вам необходимо проверить свои диапазоны дат, чтобы конец никогда не был раньше начала, что не является проблемой для гораздо более сложного необратимого метода.
Спасибо за ответ. Предположим, мы используем систему бронирования. Как изменить SQL, если мы хотим, чтобы покупатель нашел free dates в системе бронирования? Особенно интересно узнать, как найти возможности для книги holes. Это означает, что у нас есть 2 резервирования в системе, например 2014-09-01 -> 2014-09-10 и 2014-09-15 -> 2014-09-20. Как найти free hole от 2014-09-10 to 2014-09-15?
Взяв ваш примерный диапазон от 06.06.1983 до 18.06.1983 и предполагая, что у вас есть столбцы с именами Начало и конец для ваших диапазонов, вы можете использовать такое предложение
where ('1983-06-06' <= end) and ('1983-06-18' >= start)
т.е. проверьте, что начало вашего тестового диапазона находится перед концом диапазона базы данных, и что конец вашего тестового диапазона находится после или в начале диапазона базы данных.
Если ваша СУБД поддерживает функцию OVERLAP (), это становится тривиальным делом - нет необходимости в собственных решениях. (В Oracle это явно работает, но недокументировано).
Эпическое решение. Работает отлично. Это синтаксис для 2 диапазонов дат (s1, e1) и (s2, e2) в Oracle: выберите 1 из двойных, где (s1, e1) перекрывается (s2, e2);
В ваших ожидаемых результатах вы говорите
06.06.1983 по 18.06.1983 = В СПИСОК
Однако этот период не содержит и не входит ни в один из периодов в вашей таблице (не в списке!) Периодов. Однако он перекрывает период с 10.06.1983 по 14.06.1983.
Вы можете найти книгу Snodgrass (http://www.cs.arizona.edu/people/rts/tdbbook.pdf) полезной: она предшествует mysql, но концепция времени не изменилась ;-)
Я создал функцию для решения этой проблемы в 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 ;
Посмотрите на следующий пример. Это будет вам полезно.
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
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;
Попробуйте это на 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];
Другой метод с использованием инструкции 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)
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) )
Добро пожаловать в Stack Overflow! Благодарим вас за этот фрагмент кода, который может оказать немедленную помощь. Правильное объяснение значительно улучшит его образовательной ценности, показывая Почему, это хорошее решение проблемы, которое сделает его более полезным для будущих читателей с похожими, но не идентичными вопросами. Пожалуйста, редактировать свой ответ, чтобы добавить пояснение и указать, какие ограничения и предположения применяются.
возможный дубликат Определите, перекрываются ли два диапазона дат