2010-03-30 7 views
56

Diese Tabelle zum Speichern von Sitzungen verwendet wird (Ereignisse):prüfen Überlappung von Datumsbereiche in 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") 
; 

Wir wollen keinen Konflikt zwischen den Bereichen haben.
Nehmen wir an, wir müssen eine neue Sitzung von 2010-01-05 bis 2010-01-25 einfügen.
Wir würden gerne die widersprüchlichen Sitzung (en) kennen.

Hier ist meine Frage:

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 
; 

Hier ist das Ergebnis:

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

Gibt es einen besseren Weg, das zu bekommen?


fiddle

+1

Ihre dritte Bedingung ist falsch. Es soll "2010-01-05" <= start_date AND "2010-01-25"> = end_date' sein. Siehe http://stackoverflow.com/a/28802972/632951 für die Visualisierung. Ihre aktuelle dritte Bedingung wird niemals ausgewertet, da die erste (und zweite) Bedingung sie bereits abdeckt. – Pacerier

Antwort

110

Ich hatte eine solche Abfrage mit einer Kalender-Anwendung ich einmal geschrieben. Ich denke, dass ich etwas so benutzten:

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

UPDATE Dies sollte auf jeden Fall ((ns, ne, es, ee) = (new_start, new_end, existing_start, existing_end)) arbeiten:

  1. ns - ne - es - ee: nicht überlappt und passt nicht (weil ne < n)
  2. ns - es - ne - ee: Überschneidungen und Spiele
  3. es - ns - ee - ne: Überschneidungen und Übereinstimmungen
  4. es - ee - ns - ne: nicht überlappt und passt nicht (weil ns> ee)
  5. es - ns - ne - ee: Überschneidungen und Streichhölzer
  6. ns - es - ee - ne: Überlappungen und Streichhölzer

Hier ist ein fiddle

+0

@Glide: Ich denke, es sollte funktionieren, aktualisierte Antwort – soulmerge

+6

Funktioniert super !, aber ich denke @Pierre de LESPINAY sucht in seiner Abfrage nach inklusiven Bereichen: WHERE new_start <= existing_end AND new_end> = existing_start; –

+11

@OsvaldoM. Wenn er das wirklich wäre, hätte er sich vor etwa 2 Jahren beschwert ... – 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') 

Diese 3 Zeilen von SQL-Klauseln umfassen die 4 Fälle von überlappenden erforderlich.

+3

guter Job Mate :) – Adrian

+3

Auch wenn das OP nicht offensichtlich nach dieser überlappenden Definition suchte, ist diese Antwort die beste Lösung für das Problem durch den Namen der Frage beschrieben. Ich habe nach dieser Überlappung gesucht, die die wahre Überlappung darstellt. – Cec

+2

Fantastisch, rettete mir massive Kopfschmerzen. –

11

Lamys Antwort ist gut, aber Sie können es ein wenig mehr optimieren.

Dadurch werden alle vier Szenarien erfasst, in denen sich die Bereiche überschneiden, und die beiden Bereiche ausgeschlossen, wo sie nicht vorhanden sind.

+0

Gibt es außer den beiden anderen oben noch andere Lösungen? – Pacerier

3

Ich hatte das ähnliche Problem konfrontiert. Mein Problem war, die Buchung zwischen einer Reihe von gesperrten Daten zu stoppen. Zum Beispiel ist die Buchung für eine Immobilie zwischen dem 2. Mai und dem 7. Mai gesperrt. Ich musste irgendeine Art von überlappendem Datum finden, um die Buchung zu erkennen und zu stoppen. Meine Lösung ähnelt Lord Javac.

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 

Lassen Sie mich wissen, wenn es nicht funktioniert.

1

zwei Intervalle wie (s1, e1) gegeben und (s2, e2) mit s1 < e1 und s2 < e2
Sie können wie folgt überlappende berechnen:

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 

wird auch funktionieren, wenn ein Intervall in der anderen.

0

Vor kurzem wurde ich mit dem gleichen Problem zu kämpfen und kam mit diesem einem einfachen Schritt zu beenden (Dies ist ein guter Ansatz oder Speicherraubend nicht sein kann) -

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) 
); 

Das half ich mit überlappenden Zeitpunkt die Einträge finde Bereiche.

Ich hoffe, das hilft jemandem.

Verwandte Themen