Zum Inhalt springen

WHERE-Klausel im Detail

Aus Wikibooks

Seitentitel: Einführung in SQL: WHERE-Klausel im Detail
(Einführung in SQL: WHERE-Klausel im Detail)
(Einführung in SQL: 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

[Bearbeiten]

Die 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

[Bearbeiten]

Größenvergleich zweier Werte

[Bearbeiten]

Der 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:

Aufgabe
Aufgabe

Suche einen Datensatz, bei dem der Wert in der Spalte ID gleich ist zu einem vorgegebenen Wert.

select * from Versicherungsnehmer
 where ID = 10;
Aufgabe
Aufgabe

Suche Datensätze, bei denen der Name kleiner als 'B' ist, also mit 'A' anfängt.

select * from Versicherungsnehmer
 where Name < 'B';
Aufgabe
Aufgabe

Suche Führerschein-Neulinge.

select * from Versicherungsnehmer
 where Fuehrerschein >= '01.01.2007';
Aufgabe
Aufgabe

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

[Bearbeiten]

Mit 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.

Aufgabe
Aufgabe

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)

[Bearbeiten]

Die 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:

Aufgabe
Aufgabe

Der Ortsname beginnt nicht mit 'B'; der Inhalt dahinter ist beliebig.

select * from Versicherungsnehmer
 where Ort NOT LIKE 'B%';
Aufgabe
Aufgabe

Der Ortsname enthält irgendwo 'alt' mit beliebigem Inhalt davor und dahinter.

select * from Versicherungsnehmer
 where Ort LIKE '%alt%';
Aufgabe
Aufgabe

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:

Aufgabe
Aufgabe

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)

[Bearbeiten]

Ein 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%';
Teil der Beschreibung ist die Zeichenkette '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

[Bearbeiten]

Wie 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 <> '';
8 Mitarbeiter mit Mobil-Nummer
select ID, Name, Vorname, Mobil
  from Mitarbeiter
 where Mobil = '';
10 Mitarbeiter ohne Mobil-Nummer

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;
10 Mitarbeiter ohne Angabe

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;
18 Mitarbeiter mit irgendeiner Angabe (auch mit "leerer" Angabe)

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 = '' );
20 Mitarbeiter ohne ausdrückliche Angabe

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

[Bearbeiten]

Der IN-Parameter vergleicht, ob der Inhalt einer Spalte in der angegebenen Liste enthalten ist. Die Liste kann mit beliebigen Datentypen arbeiten.

Aufgabe
Aufgabe

Hole die Liste aller Fahrzeuge, deren Typen als „VW-Kleinwagen“ registriert sind.

select * from Fahrzeug
 where Fahrzeugtyp_ID in (1, 2);
Aufgabe
Aufgabe

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.

Aufgabe
Aufgabe

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

[Bearbeiten]

Im 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:

Liste aller Fahrzeuge vom Typ 'Volkswagen'
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

[Bearbeiten]

Bei 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

[Bearbeiten]

Dieser Operator kehrt das Ergebnis um: aus TRUE wird FALSE, aus FALSE wird TRUE.

Siehe oben: keine Führerschein-Neulinge
SELECT * FROM Versicherungsnehmer
 WHERE NOT (Fuehrerschein >= '01.01.2007');

AND als Konjunktion

[Bearbeiten]

Eine Bedingung, die durch eine AND-Verknüpfung gebildet wird, ist genau dann TRUE, wenn beide (bzw. alle) Bestandteile TRUE sind.

Die nach Alphabet erste Hälfte der Versicherungsnehmer eines PLZ-Bereichs
SELECT ID, Name, Vorname, PLZ, Ort 
  FROM Versicherungsnehmer
 WHERE PLZ BETWEEN '45000' AND '45999'
   AND Name < 'K';

OR als Adjunktion

[Bearbeiten]

Eine 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.

Die nach Alphabet erste Hälfte der Versicherungsnehmer und alle eines PLZ-Bereichs
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

[Bearbeiten]

Eine 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.

MySQL-Version: Die nach Alphabet erste Hälfte der Versicherungsnehmer oder alle eines PLZ-Bereichs
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?

[Bearbeiten]

Bereits 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

[Bearbeiten]

In 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

[Bearbeiten]

Bei 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ösungen

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

[Bearbeiten]

Dieses Kapitel verweist auf die folgenden Kapitel:

Bei Wikipedia gibt es weitere fachliche Hinweise: