Ta tabela służy do przechowywania sesji (zdarzeń):
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")
;
Nie chcemy mieć konfliktu między zakresami.
Powiedzmy, że musimy wstawić nową sesję od 2010-01-05 do 2010-01-25 .
Chcielibyśmy poznać sesje powodujące konflikt.
Oto moje zapytanie:
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
;
Oto wynik:
+----+------------+------------+
| id | start_date | end_date |
+----+------------+------------+
| 1 | 2010-01-01 | 2010-01-10 |
| 2 | 2010-01-20 | 2010-01-30 |
+----+------------+------------+
Czy jest lepszy sposób, aby to osiągnąć?
mysql
range
overlapping-matches
Pierre de LESPINAY
źródło
źródło
"2010-01-05" <= start_date AND "2010-01-25" >= end_date
. Zobacz stackoverflow.com/a/28802972/632951 w celu uzyskania wizualizacji. Twój obecny trzeci warunek nigdy nie zostanie oceniony, ponieważ pierwszy (i drugi) warunek już go obejmuje.Odpowiedzi:
Miałem takie zapytanie w aplikacji kalendarza, którą kiedyś napisałem. Myślę, że użyłem czegoś takiego:
UPDATE To zdecydowanie powinno działać ((ns, ne, es, ee) = (new_start, new_end, existing_start, existing_end)):
Oto skrzypce
źródło
=
do swojego kodu, zamiast zawracać sobie głowę narzekaniem.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')
Te 3 wiersze klauzul sql obejmują 4 wymagane przypadki nakładania się.
źródło
Odpowiedź Lamy jest dobra, ale możesz ją nieco bardziej zoptymalizować.
SELECT * FROM tbl WHERE existing_start BETWEEN $newSTart AND $newEnd OR $newStart BETWEEN existing_start AND existing_end
Spowoduje to wyłapanie wszystkich czterech scenariuszy, w których zakresy się pokrywają, i wykluczenie dwóch, w których ich nie ma.
źródło
Miałem podobny problem. Mój problem polegał na tym, że zatrzymałem rezerwację między różnymi zablokowanymi datami. Na przykład rezerwacja jest zablokowana dla nieruchomości między 2 a 7 maja. Musiałem znaleźć jakiekolwiek nakładające się daty, aby wykryć i zatrzymać rezerwację. Moje rozwiązanie jest podobne do 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
Daj mi znać, jeśli to nie zadziała.
źródło
Biorąc pod uwagę dwa przedziały, takie jak (s1, e1) i (s2, e2) z s1 <e1 i s2 <e2,
możesz obliczyć nakładanie się w następujący sposób:
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
Będzie również działać, jeśli jeden interwał jest w drugim.
źródło
Ostatnio zmagałem się z tym samym problemem i zakończyłem tym jednym łatwym krokiem (może to nie być dobre podejście lub pochłaniające pamięć) -
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) );
Pomogło mi to znaleźć wpisy z nakładającymi się zakresami dat.
Mam nadzieję, że to komuś pomoże.
źródło
Możesz objąć wszystkie przypadki nakładania się dat, nawet jeśli data w bazie danych może być zerowa w następujący sposób:
SELECT * FROM `tableName` t WHERE t.`startDate` <= $toDate AND (t.`endDate` IS NULL OR t.`endDate` >= $startDate);
Spowoduje to zwrócenie wszystkich rekordów, które i tak pokrywają się z nowymi datami rozpoczęcia / zakończenia.
źródło
Powyższa odpowiedź Mackrakena jest lepsza z punktu widzenia wydajności, ponieważ nie wymaga kilku operacji operacyjnych, aby ocenić, czy dwie daty się pokrywają. Niezłe rozwiązanie!
Jednak odkryłem, że w MySQL musisz użyć DATEDIFF zamiast operatora minus -
SELECT o.orderStart, o.orderEnd, s.startDate, s.endDate , GREATEST(LEAST(orderEnd, endDate) - GREATEST(orderStart, startDate), 0)>0 as overlaps , DATEDIFF(LEAST(orderEnd, endDate), GREATEST(orderStart, startDate)) as overlap_length FROM orders o JOIN dates s USING (customerId) WHERE 1 AND DATEDIFF(LEAST(orderEnd, endDate),GREATEST(orderStart, startDate)) > 0;
źródło