WHERE-Klausel im Detail
In diesem Kapitel werden die Einzelheiten der WHERE-Klausel genauer behandelt. Diese Angaben sind vor allem für den SELECT-Befehl, aber auch für UPDATE und DELETE von Bedeutung.
Die Beispiele beziehen sich auf den Anfangsbestand der Beispieldatenbank; auf die Ausgabe der selektierten Datensätze wird verzichtet. Bitte probieren Sie alle Beispiele aus und nehmen Sie verschiedene Änderungen vor, um die Auswirkungen zu erkennen.
Allgemeine Hinweise
BearbeitenDie WHERE-Klausel ist (neben der Verknüpfung mehrerer Tabellen) der wichtigste Bestandteil des SELECT-Befehls: Je sorgfältiger die Auswahlbedingungen formuliert werden, desto genauer ist das Ergebnis der Abfrage.
Neben den hier erläuterten Varianten bietet jedes DBMS noch andere, z. B. STARTING WITH oder SIMILAR.
Anstelle konstanter Werte können auch passende Ausdrücke angegeben werden, z. B. Funktionen oder Unterabfragen.
Verwenden Sie bei den Beispielen möglichst immer auch die Umkehrung der Auswahl mit bzw. ohne NOT. Wie im Kapitel „Ausführliche SELECT-Struktur“ zur WHERE-Klausel angegeben, steht das NOT ggf. unmittelbar vor dem Parameter-Namen.
Eine einzelne Bedingung
BearbeitenGrößenvergleich zweier Werte
BearbeitenDer einfachste Weg ist der direkte Vergleich zweier Werte, nämlich der Inhalt einer Spalte mit einem konstanten Wert. Dies ist möglich mit den folgenden Vergleichsoperatoren, und zwar für alle Datentypen, die verglichen werden können – Zahlen, Zeichenketten, Datumsangaben.
= < > <= >= <>
Beispiele:
Suche einen Datensatz, bei dem der Wert in der Spalte ID gleich ist zu einem vorgegebenen Wert.
select * from Versicherungsnehmer
where ID = 10;
Suche Datensätze, bei denen der Name kleiner als 'B' ist, also mit 'A' anfängt.
select * from Versicherungsnehmer
where Name < 'B';
Suche Führerschein-Neulinge.
select * from Versicherungsnehmer
where Fuehrerschein >= '01.01.2007';
Suche Fahrzeugtypen mit kurzer Bezeichnung.
select * from Fahrzeugtyp
where Char_Length(Bezeichnung) <= 3;
Bei diesen Vergleichen ist NOT zwar ebenfalls möglich; besser verständlich ist aber ein anderer passender Operator.
BETWEEN AND – Werte zwischen zwei Grenzen
BearbeitenMit der Bedingung BETWEEN <wert1> AND <wert2> wird direkt mit einem Bereich verglichen; die Grenzwerte gehören meistens zum Bereich (abhängig vom DBMS). Auch dies ist möglich für Zahlen, Zeichenketten, Datumsangaben.
Suche Datensätze, bei denen die PLZ außerhalb eines Bereichs 45000...45999 liegt.
select * from Versicherungsnehmer
where PLZ NOT BETWEEN '45000' AND '45999';
LIKE – Ähnlichkeiten (1)
BearbeitenDie LIKE-Bedingung vergleicht Zeichenketten „ungenau“: Der gesuchte Text soll als Wert in einer Spalte enthalten sein; dazu werden „Wildcards“ benutzt: Der Unterstrich '_' steht für ein beliebiges einzelnes Zeichen, das an der betreffenden Stelle vorkommen kann. Das Prozentzeichen '%' steht für eine beliebige Zeichenkette mit 0 oder mehr Zeichen.
Diese Bedingung wird vor allem in zwei Situationen gerne benutzt:
- Der Suchbegriff ist sehr lang; dem Anwender soll es genügen, den Anfang einzugeben.
- Der Suchbegriff ist nicht genau bekannt (z. B. nicht richtig lesbar).
Beispiele:
Der Ortsname beginnt nicht mit 'B'; der Inhalt dahinter ist beliebig.
select * from Versicherungsnehmer
where Ort NOT LIKE 'B%';
Der Ortsname enthält irgendwo 'alt' mit beliebigem Inhalt davor und dahinter.
select * from Versicherungsnehmer
where Ort LIKE '%alt%';
Der Anfangsbuchstabe des Namens ist unklar, aber danach folgen die Buchstaben 'ei' und noch etwas mehr.
select * from Versicherungsnehmer
where Name LIKE '_ei%';
Ein Problem haben wir, wenn eines der Wildcard-Zeichen Teil des Suchbegriffs sein soll. Dann muss dem LIKE-Parameter mitgeteilt werden, dass '%' bzw. '_' als „echtes“ Zeichen zu verstehen ist. Das geschieht dadurch, dass ein spezielles Zeichen davor gesetzt wird und dieses Zeichen als „ESCAPE-Zeichen“ angegeben wird:
Innerhalb der Beschreibung kommt die Zeichenfolge '10%' vor.
select * from Schadensfall
where Beschreibung LIKE '%10\%%' ESCAPE '\';
Das erste und das letzte Prozentzeichen stehen dafür, dass vorher und nachher beliebige Inhalte möglich sind. Das mittlere Prozentzeichen wird mit dem Escape-Zeichen '\' verbunden und ist damit Teil der gesuchten Zeichenfolge. Diese Angabe '\%' ist als ein Zeichen zu verstehen.
Vergleichen Sie das Abfrageergebnis, wenn der ESCAPE-Parameter weggelassen wird oder wenn eines oder mehrere der Sonderzeichen im LIKE-Parameter fehlen.
CONTAINS u. a. – Ähnlichkeiten (2)
BearbeitenEin Problem des LIKE-Parameters ist die Verwendung der Wildcard-Zeichen '%' und '_', die man gerne vergisst oder (wie im letzten Beispiel) nicht genau genug beachtet. Deshalb gibt es verschiedene Vereinfachungen.
CONTAINS – in Firebird CONTAINING – prüft, ob eine Zeichenkette im Feldinhalt enthalten ist.
select * from Schadensfall
where Beschreibung CONTAINS '10%';
Bitte prüfen Sie in der Beschreibung Ihres DBMS, welche Möglichkeiten für die Suche nach Ähnlichkeiten außerdem angeboten werden.
IS NULL – null-Werte prüfen
BearbeitenWie schon bei den relationalen Datenbanken besprochen, haben NULL-Werte eine besondere Bedeutung. Mit den folgenden beiden Abfragen werden nicht alle Datensätze gefunden:
select ID, Name, Vorname, Mobil
from Mitarbeiter
where Mobil <> '';
select ID, Name, Vorname, Mobil
from Mitarbeiter
where Mobil = '';
Nanu, es gibt doch 28 Mitarbeiter; wo sind die übrigen geblieben? Für diese Fälle gibt es mit IS NULL eine spezielle Abfrage:
select ID, Name, Vorname, Mobil
from Mitarbeiter
where Mobil is null;
Der Vollständigkeit halber sei darauf hingewiesen, dass die folgende Abfrage tatsächlich die richtige Gegenprobe liefert.
select ID, Name, Vorname, Mobil
from Mitarbeiter
where Mobil is not null;
Die folgende Abfrage liefert eine leere Ergebnismenge zurück, weil NULL eben kein Wert ist.
select ID, Name, Vorname, Mobil
from Mitarbeiter
where Mobil = null;
Es gibt keine einzelne Bedingung, die alle Datensätze ohne explizite Mobil-Angabe auf einmal angibt. Es gibt nur die Möglichkeit, die beiden Bedingungen "IS NULL" und "ist leer" zu verknüpfen:
select ID, Name, Vorname, Mobil
from Mitarbeiter
where ( Mobil is null ) or ( Mobil = '' );
Beachten Sie auch bei "WHERE ... IS [NOT] NULL" die Bedeutung von NULL:
- Bei Zeichenketten ist zu unterscheiden zwischen dem „leeren“ String und dem NULL-Wert.
- Bei Zahlen ist zu unterscheiden zwischen der Zahl '0' (null) und dem NULL-Wert.
- Bei Datumsangaben ist zu unterscheiden zwischen einem vorhandenen Datum und dem NULL-Wert; ein Datum, das der Zahl 0 entspräche, gibt es nicht. (Man könnte allenfalls das kleinste mögliche Datum wie '01.01.0100' benutzen, aber dies ist bereits ein Datum.)
IN – genauer Vergleich mit einer Liste
BearbeitenDer IN-Parameter vergleicht, ob der Inhalt einer Spalte in der angegebenen Liste enthalten ist. Die Liste kann mit beliebigen Datentypen arbeiten.
Hole die Liste aller Fahrzeuge, deren Typen als „VW-Kleinwagen“ registriert sind.
select * from Fahrzeug
where Fahrzeugtyp_ID in (1, 2);
Suche nach einem Unfall Fahrzeuge mit einer von mehreren möglichen Farben.
select * from Fahrzeug
where Farbe in ('ocker', 'gelb');
Vor allem das erste Beispiel wird sehr oft mit einer Unterabfrage versehen; vergleichen Sie dazu auch den folgenden Abschnitt zu EXISTS.
Hole die Liste aller Fahrzeuge vom Typ „Volkswagen“.
select * from Fahrzeug
where Fahrzeugtyp_ID in
( select ID from Fahrzeugtyp
where Hersteller_ID = 1 );
Dabei wird zuerst mit der Unterabfrage eine Liste aller Fahrzeugtypen-IDs für den Hersteller 1 (= Volkswagen) zusammengestellt; diese wird dann für den Vergleich über den IN-Parameter benutzt.
EXISTS – schneller Vergleich mit einer Liste
BearbeitenIm Gegensatz zu den anderen Parametern der WHERE-Klausel arbeitet der EXISTS-Parameter nicht mit fest vorgegebenen Werten, sondern nur mit dem Ergebnis einer Abfrage, also einer Unterabfrage. Das letzte Beispiel zum IN-Parameter kann auch so formuliert werden:
select * from Fahrzeug fz
where EXISTS
( select * from Fahrzeugtyp ft
where ft.Hersteller_ID = 1
and fz.Fahrzeugtyp_ID = ft.ID );
Zu jedem Datensatz aus der Tabelle Fahrzeug wird zu dieser Fahrzeugtyp_ID eine Unterabfrage aus den Fahrzeugtypen erstellt: Wenn es dort einen Datensatz mit passender ID und Hersteller-ID 1 (= Volkswagen) gibt, gehört der Fahrzeug-Datensatz zur Auswahl, andernfalls nicht.
Da Unterabfragen zuerst ausgeführt werden, wird eine EXISTS-Prüfung in aller Regel schneller erledigt als die entsprechende IN-Prüfung: Bei EXISTS handelt es sich um eine Feststellung „ist überhaupt etwas vorhanden“; bei IN dagegen muss ein exakter Vergleich mit allen Werten einer Liste durchgeführt werden. Bei unserer kleinen Beispieldatenbank spielt das natürlich keine Rolle, aber bei einer „echten“ Datenbank mit Millionen von Einträgen schon.
Mehrere Bedingungen verknüpfen
BearbeitenBei der WHERE-Klausel geht es darum festzustellen, ob ein Datensatz Teil des Abfrageergebnisses ist oder nicht; bei der <search condition> handelt sich also um einen booleschen Ausdruck, d. h. einen Ausdruck, der einen der booleschen Werte WAHR oder FALSCH – TRUE bzw. FALSE – als Ergebnis hat. Nur bei einfachen Abfragen genügt dazu eine einzelne Bedingung; meistens müssen mehrere Bedingungen verknüpft werden (wie beim letzten Beispiel unter IS NULL).
Dazu gibt es die booleschen Operatoren NOT, AND, OR.
NOT als Negation
BearbeitenDieser Operator kehrt das Ergebnis um: aus TRUE wird FALSE, aus FALSE wird TRUE.
SELECT * FROM Versicherungsnehmer
WHERE NOT (Fuehrerschein >= '01.01.2007');
AND als Konjunktion
BearbeitenEine Bedingung, die durch eine AND-Verknüpfung gebildet wird, ist genau dann TRUE, wenn beide (bzw. alle) Bestandteile TRUE sind.
SELECT ID, Name, Vorname, PLZ, Ort
FROM Versicherungsnehmer
WHERE PLZ BETWEEN '45000' AND '45999'
AND Name < 'K';
OR als Adjunktion
BearbeitenEine Bedingung, die durch eine OR-Verknüpfung gebildet wird, ist genau dann TRUE, wenn mindestens ein Bestandteil TRUE ist; dabei ist es gleichgültig, ob die anderen Bestandteile TRUE oder FALSE sind.
SELECT ID, Name, Vorname, PLZ, Ort
FROM Versicherungsnehmer
WHERE PLZ BETWEEN '45000' AND '45999'
OR Name < 'K';
Bitte beachten Sie, dass der normale Sprachgebrauch „alle ... und alle ...“ sagt. Gemeint ist nach logischen Begriffen aber, dass <Bedingung 1> erfüllt sein muss ODER <Bedingung 2> ODER BEIDE.
XOR als Kontravalenz
BearbeitenEine Bedingung, die durch eine XOR-Verknüpfung gebildet wird, ist genau dann TRUE, wenn ein Bestandteil TRUE ist, aber der andere Bestandteil FALSE ist – „ausschließendes oder“ bzw. „entweder – oder“. Diese Verknüpfung gibt es selten, z. B. bei MySQL; hier wird es der Vollständigkeit halber erwähnt.
SELECT ID, Name, Vorname, PLZ, Ort
FROM Versicherungsnehmer
WHERE PLZ BETWEEN '45000' AND '45999'
XOR Name < 'K';
Bitte beachten Sie, dass hier der normale Sprachgebrauch „oder“ sagt und „entweder – oder“ gemeint ist.
Anstelle von XOR kann immer eine Kombination verwendet werden:
( <Bedingung 1> AND ( NOT <Bedingung 2> ) ) OR ( <Bedingung 2> AND ( NOT <Bedingung 1> ) )
Klammern benutzen oder weglassen?
BearbeitenBereits im Kapitel „Ausführliche SELECT-Struktur“ wurde die Hierarchie genannt:
- NOT ist die engste Verbindung und wird vorrangig ausgewertet.
- AND ist die nächststärkere Verbindung und wird danach ausgewertet.
- OR ist die schwächste Verbindung und wird zuletzt ausgewertet.
- Was in Klammern steht, wird vor allem anderen ausgewertet.
Bitte setzen Sie im folgenden Beispiel Klammern an anderen Stellen oder streichen Sie Klammern, und vergleichen Sie die Ergebnisse.
SELECT ID, Name, Vorname, PLZ, Ort
FROM Versicherungsnehmer
WHERE not ( PLZ BETWEEN '45000' AND '45999'
AND ( Name LIKE 'B%'
OR Name LIKE 'K%'
OR NOT Name CONTAINING 'ei'
)
)
order by PLZ, Name;
Sie werden ziemlich unterschiedliche Ergebnisse feststellen. Es empfiehlt sich deshalb, an allen sinnvollen Stellen Klammern zu setzen – auch dort, wo sie nicht erforderlich sind – und das, was zusammengehört, durch Einrückungen sinnvoll zu gliedern.
Zusammenfassung
BearbeitenIn diesem Kapitel lernten wir neben dem Vergleich von Werten viele Möglichkeiten kennen, mit denen Bedingungen für Abfragen festgelegt werden können:
- Mit BETWEEN AND werden Werte innerhalb eines Bereichs geprüft.
- Mit LIKE und CONTAINS werden Werte gesucht, die mit vorgegebenen Werten teilweise übereinstimmen.
- Mit IS NULL werden null-Werte gesucht.
- Mit IN und EXISTS werden Spaltenwerte mit einer Liste verglichen.
Mit AND, OR, NOT werden Bedingungen zusammengefasst.
Übungen
BearbeitenBei den folgenden Aufgaben kommt es nur auf die WHERE-Klausel an; Sie dürfen ein SELECT „mit allen Spalten“ benutzen.
Übung 1 | Auswahl nach Zeichenketten | Zur Lösung |
Suchen Sie alle Versicherungsnehmer, die folgenden Bedingungen entsprechen:
- Der erste Buchstabe des Nachnamens ist nicht bekannt, der zweite ist ein 'r'.
- Der Vorname enthält ein 'a'.
- Die Postleitzahl gehört zum Bereich Essen (PLZ 45...).
Übung 2 | Auswahl nach Datumsbereich | Zur Lösung |
Suchen Sie alle Versicherungsnehmer, die in den Jahren 1967 bis 1970 ihren 18. Geburtstag hatten.
Übung 3 | Auswahl nach Ähnlichkeit | Zur Lösung |
Zeigen Sie alle Schadensfälle an, bei denen in der Beschreibung auf eine prozentuale Angabe hingewiesen wird.
Übung 4 | Auswahl für unbekannte Werte | Zur Lösung |
Zeigen Sie alle Dienstwagen an, die keinem Mitarbeiter persönlich zugeordnet sind.
Hinweis: Die Prüfung „Mitarbeiter ohne Dienstwagen“ ist komplizierter; das dafür erforderliche OUTER JOIN wird erst später behandelt.
Übung 5 | Bedingungen verknüpfen | Zur Lösung |
Zeigen Sie alle Mitarbeiter der Abteilungen „Vertrieb“ (= 'Vert') und „Ausbildung“ (= 'Ausb') an.
Hinweis: Bestimmen Sie zunächst die IDs der gesuchten Abteilungen und benutzen Sie das Ergebnis für die eigentliche Abfrage.
Übung 6 | Bedingungen verknüpfen | Zur Lösung |
Gesucht werden die Versicherungsverträge für Haftpflicht (= 'HP') und Teilkasko (= 'TK'), die mindestens seit dem Ende des Jahres 1980 bestehen und aktuell nicht mit dem minimalen Prämiensatz berechnet werden.
Hinweis: Tragen Sie ausnahmsweise nur die notwendigen Klammern ein, nicht alle sinnvollen.
Lösung zu Übung 1 | Auswahl nach Zeichenketten | Zur Übung |
select * from Versicherungsnehmer
where Name like '_r%' and Vorname like '%a%'
and PLZ STARTING WITH '45' /* oder: */
PLZ like '45%';
Lösung zu Übung 2 | Auswahl nach Datumsbereich | Zur Übung |
select * from Versicherungsnehmer
where DATEADD(YEAR, 18, Geburtsdatum) BETWEEN '01.01.1967' AND '31.12.1970';
Lösung zu Übung 3 | Auswahl nach Ähnlichkeit | Zur Übung |
SELECT * from Schadensfall
where Beschreibung like '%\%%' escape '\';
Lösung zu Übung 4 | Auswahl für unbekannte Werte | Zur Übung |
SELECT * from Dienstwagen
where Mitarbeiter_ID is null;
Lösung zu Übung 5 | Bedingungen verknüpfen | Zur Übung |
SELECT * from Mitarbeiter
where Abteilung_ID in (
select id from Abteilung
where Kuerzel in ('Vert', 'Ausb') );
Lösung zu Übung 6 | Bedingungen verknüpfen | Zur Übung |
SELECT * from Versicherungsvertrag
where (Art = 'HP' or Art = 'TK')
and Abschlussdatum <= '31.12.1980'
and (not Praemiensatz = 30) /* oder */
and Praemiensatz > 30;
Siehe auch
BearbeitenDieses Kapitel verweist auf die folgenden Kapitel:
Bei Wikipedia gibt es weitere fachliche Hinweise: