2010-03-30 4 views
56

Эта таблица используется для хранения сессий (события):Проверка перекрытия диапазонов дат в MySQL

CREATE TABLE session (
    id int(11) NOT NULL AUTO_INCREMENT 
, start_date date 
, end_date date 
); 

INSERT INTO session 
    (start_date, end_date) 
VALUES 
    ("2010-01-01", "2010-01-10") 
, ("2010-01-20", "2010-01-30") 
, ("2010-02-01", "2010-02-15") 
; 

Мы не хотим, чтобы конфликт между диапазонами.
Предположим, что нам необходимо вставить новый сеанс из 2010-01-05 - 2010-01-25.
Мы хотели бы знать конфликтующие сессии.

Вот мой запрос:

SELECT * 
FROM session 
WHERE "2010-01-05" BETWEEN start_date AND end_date 
    OR "2010-01-25" BETWEEN start_date AND end_date 
    OR "2010-01-05" >= start_date AND "2010-01-25" <= end_date 
; 

Вот результат:

+----+------------+------------+ 
| id | start_date | end_date | 
+----+------------+------------+ 
| 1 | 2010-01-01 | 2010-01-10 | 
| 2 | 2010-01-20 | 2010-01-30 | 
+----+------------+------------+ 

Есть ли лучший способ, чтобы получить это?


fiddle

+1

Ваше третье условие неверно. Предполагается, что это '' 2010-01-05 "<= start_date AND" 2010-01-25 "> = end_date'. См. Http://stackoverflow.com/a/28802972/632951 для визуализации. Ваше текущее третье условие никогда не будет оцениваться, потому что первое (и второе) условие уже покрывает его. – Pacerier

ответ

110

У меня был такой запрос с приложением календаря я когда-то писал. Я думаю, что я что-то вроде этого:

... WHERE new_start < existing_end 
     AND new_end > existing_start; 

UPDATE Это должно определенно работать ((нс, пе, эс, EE) = (new_start, new_end, existing_start, existing_end)):

  1. нс - п - эс - й: не перекрывает друг друга и не соответствует (потому что пе < ы)
  2. нса - эс - п - ЕЕ: перекрывается и спички
  3. ES - NS - ЭО - Ne: перекрытия и совпадения
  4. эс - Е.Е. - нс - п: не перекрывает друг друга и не соответствует (потому что нс> Е.Е.)
  5. эс - нс - п - Е.Е.: перекрывается и соответствует
  6. нса - эс - й - п: частично совпадающих и спички

Вот fiddle

+0

@Glide: Я думаю, что он должен работать, обновленный ответ – soulmerge

+6

Отлично работает !, но я думаю, что @Pierre de LESPINAY ищет в своем запросе инклюзивные диапазоны: WHERE new_start <= existing_end AND new_end> = existing_start; –

+11

@OsvaldoM. Если бы он был на самом деле, он бы жаловался около 2 лет назад ... – soulmerge

18
SELECT * FROM tbl WHERE 
existing_start BETWEEN $newStart AND $newEnd OR 
existing_end BETWEEN $newStart AND $newEnd OR 
$newStart BETWEEN existing_start AND existing_end 

if (!empty($result)) 
throw new Exception('We have overlapping') 

Эти 3 линии SQL положений охватывают 4 случая перекрывающихся требуется.

+3

good job mate :) – Adrian

+3

Даже если OP, по-видимому, не искал это перекрывающееся определение, этот ответ является лучшим решением проблемы, описываемой именем вопроса. Я искал это перекрытие, которое является истинным перекрытием. – Cec

+2

Фантастический, спас меня от массивной головной боли. –

11

Ответ Лами хороший, но вы можете оптимизировать его немного больше.

SELECT * FROM tbl WHERE 
existing_start BETWEEN $newSTart AND $newEnd OR 
$newStart BETWEEN existing_start AND existing_end 

Это поймает все четыре сценария, где диапазоны перекрываются и исключают два, где они не находятся.

+0

Существуют ли другие решения помимо этого и двух других выше? – Pacerier

3

У меня возникла аналогичная проблема. Моя проблема состояла в том, чтобы остановить бронирование между диапазоном заблокированных дат. Например, бронирование заблокировано для собственности от 2-го мая до 7-го мая. Мне нужно было найти какую-то перекрывающуюся дату для обнаружения и остановки бронирования. Мое решение похоже на LordJavac.

SELECT * FROM ib_master_blocked_dates WHERE venue_id=$venue_id AND 
(
    (mbd_from_date BETWEEN '$from_date' AND '$to_date') 
    OR 
    (mbd_to_date BETWEEN '$from_date' AND '$to_date') 
    OR 
    ('$from_date' BETWEEN mbd_from_date AND mbd_to_date) 
    OR  
    ('$to_date' BETWEEN mbd_from_date AND mbd_to_date)  
) 
*mbd=master_blocked_dates 

Сообщите мне, если он не работает.

1

Учитывая два интервала как (s1, e1) и (s2, е2) с s1 < e1 и s2 < e2
Вы можете вычислить перекрытием, как это:

SELECT 
    s1, e1, s2, e2, 
    ABS(e1-s1) as len1, 
    ABS(e2-s2) as len2, 
    GREATEST(LEAST(e1, e2) - GREATEST(s1, s2), 0)>0 as overlaps, 
    GREATEST(LEAST(e1, e2) - GREATEST(s1, s2), 0) as overlap_length 
FROM test_intervals 

также будет работать, если один интервал в другой.

0

Недавно я боролся с той же проблемой и пришел конец с этим один шаг (Это не может быть хорошим подходом или потребление памяти) -

SELECT * FROM duty_register WHERE employee = '2' AND (
(
duty_start_date BETWEEN {$start_date} AND {$end_date} 
OR 
duty_end_date BETWEEN {$start_date} AND {$end_date} 
) 
OR 
(
{$start_date} BETWEEN duty_start_date AND duty_end_date 
OR 
{$end_date} BETWEEN duty_start_date AND duty_end_date) 
); 

Это помогло мне найти записи с перекрытием даты диапазоны.

Надеюсь, это поможет кому-то.

Смежные вопросы