Bietet Rezepte, Lösungen und ausführliche Beispiele rund um gesundes SQL und zufriedene Datenbanken.
Es sollen Tischreservierungen geprüft werden. Der Anwender kann einen Zeitraum (von/bis Uhrzeit) eingeben und prüfen lassen, ob in dieser Zeit ein Tisch frei ist. Die SQL-Abfrage muß sicherstellen, dass es zu keinen zeitlichen Überschneidungen kommt.
* MySQL 5.1.30
* IBM DB2 9.1
Die Datenbank-Tabellen hierfür sehen wie folgt aus.
/**
* Stammdaten Tische
*/
CREATE TABLE tisch (
tisch_id INT NOT NULL,
PRIMARY KEY (tisch_id)
);
/**
* Tisch-Reservierungen
*/
CREATE TABLE tisch_reservierung (
id INT NOT NULL,
tisch_id INT NOT NULL,
von TIMESTAMP NOT NULL,
bis TIMESTAMP NOT NULL,
PRIMARY KEY (id),
CONSTRAINT fk_tisch_reserv_01
FOREIGN KEY (tisch_id)
REFERENCES tisch (tisch_id)
);
Nachdem die Tabellen erzeugt sind, können die Stammdaten, zum Beispiel diese vier Testdatensätze für die Tische, eingefügt werden.
/**
* Einfügen von vier Testdatensätzen
*/
INSERT INTO tisch VALUES
( 1 ), ( 2 ), ( 3 ), ( 4 );
Im nächsten Schritt werden ein paar Tischreservierungen eingefügt. Auf Basis dieser Testdaten versuchen wir später die Abfragen zu erstellen.
/**
* Einfügen von sechs Testreservierungen als Multi-Row-Insert
*/
INSERT INTO tisch_reservierung
( id, tisch_id, von, bis ) VALUES
( 1, 1, '2010-06-13 09:00:00', '2010-06-13 11:00:00'),
( 2, 2, '2010-06-13 09:00:00', '2010-06-13 11:00:00'),
( 3, 3, '2010-06-13 09:00:00', '2010-06-13 11:00:00'),
( 4, 1, '2010-06-13 12:00:00', '2010-06-13 15:00:00'),
( 5, 2, '2010-06-13 13:00:00', '2010-06-13 16:00:00'),
( 6, 4, '2010-06-13 09:00:00', '2010-06-13 13:00:00');
Nun kommen wir zum eigentlichen Thema und zwar zu der SQL-Abfrage. Wie muss die SQL-Abfrage aussehen, die die Tische liefert, die am 13.06.2010 zwischen 11:15 und 12:45 frei sind. Hört sich im ersten Moment leichter an, als es ist. Aber fangen wir einfach mal an.
Wenn wir die schon vorhandenen Reservierungen grafisch aufbereiten, läßt sich ziemlich schnell erkennen, dass zwischen 11:15 und 12:45 nur zwei Tische frei wären. Die Tische zwei oder drei wären verfügbar.
1.) Unsere SQL-Abfrage soll sicher stellen, dass kein Reservierungsstart (von) und kein Reservierungsende (bis) im gewünschen Zeitbereich 11:15 und 12:45 liegt (Listing 4:).
SELECT t.tisch_id
FROM tisch t
WHERE t.tisch_id NOT IN
( SELECT tisch_id FROM tisch_reservierung
WHERE von BETWEEN '2010-06-13 11:15:00' AND '2010-06-13 12:45:00' )
AND t.tisch_id NOT IN
( SELECT tisch_id FROM tisch_reservierung
WHERE bis BETWEEN '2010-06-13 11:15:00' AND '2010-06-13 12:45:00' )
ORDER BY t.tisch_id;
+----------+
| tisch_id |
+----------+
| 2 |
| 3 |
| 4 |
+----------+
3 rows in set (0.00 sec)
mysql>
Überraschenderweise liefert die Abfrage (Listing 4:) neben den erwarteten Tischen zwei und drei auch der Tisch vier. Der erste Tisch wird korrekt als belegt erkannt. Aber leider wird auch den Tisch vier als frei gemeldet, obwohl doch klar ist, dass nur die Tische zwei und drei frei sind. Was ist passiert? Die Reservierung des Tisches vier überlappt den gewünschten Bereich komplett und kein Start-/Endewert liegt innerhalb des abgefragten Zeitbereichs! Unser erste Abfrage greift also zu kurz.
2.) Starten wir also einen zweiten Versuch und berücksichtigen nun zusätzlich, ob die abgefragten Start-/Endezeiten innerhalb eines bereits reservierten Zeitraumes liegen.
SELECT t.tisch_id
FROM tisch t
WHERE t.tisch_id NOT IN
( SELECT tisch_id FROM tisch_reservierung
WHERE von BETWEEN '2010-06-13 11:15:00' AND '2010-06-13 12:45:00' )
AND t.tisch_id NOT IN
( SELECT tisch_id FROM tisch_reservierung
WHERE bis BETWEEN '2010-06-13 11:15:00' AND '2010-06-13 12:45:00' )
AND t.tisch_id NOT IN
( SELECT tisch_id FROM tisch_reservierung
WHERE '2010-06-13 11:15:00' BETWEEN von AND bis )
AND t.tisch_id NOT IN
( SELECT tisch_id FROM tisch_reservierung
WHERE '2010-06-13 12:45:00' BETWEEN von AND bis )
ORDER BY t.tisch_id;
+----------+
| tisch_id |
+----------+
| 2 |
| 3 |
+----------+
2 rows in set (0.13 sec)
mysql>
Mit unserer zweiten Abfrage kommen wir ans Ziel. Diese Abfrage prüft mit Hilfe von BETWEEN alle vier "Zeitecken" auf Überschneidungen mit anderen Reservierungen ab. So läßt sich sicherstellen, dass es keine "doppelten" Reservierungen gibt.