|
Home - Programmieren - Datenbanken - SQL-DQL: Der SELECT-Befehl
Hinweis: Für den hier dargestellte Inhalt ist nicht der Betreiber der Plattform, sondern der jeweilige Autor verantwortlich. Falls Sie Missbrauch vermuten, bitten wir Sie, uns unter missbrauch@it-academy.cc zu kontaktieren. [Druckansicht] [Als E-Mail senden] [Kommentar verfassen]
Die DQL (Data Query Language) ist der Teil von SQL, mit dem sich Daten aus Tabellen oder Views abfragen lassen. Die DQL umfasst den Syntax:
Eine Die einzelnen Syntaxelemente werden in den nächsten Abschnitten einzeln erläutert. Die AusdruckslisteJeder Ausdruck in der Ausdrucksliste wird bei erfolgter Abfrage zu einer Spalte in der Ergebnismenge. Es können beliebig viele Ausdrücke angegeben werden, diese werden dann jeweils durch ein Komma voneinander getrennt. Beispiel:
Möchte man sämtliche Spalten einer Datenquelle abfragen, so kann dies mit der Wildcard * bewerkstelligt werden:
Tabellennamen qualifizieren
Möchte man gleichnamige Spalten aus verschiedenen Datenquellen abfragen, so sind diese Spalten mit dem Tabellennamen zu qualifizieren. Diese Angabe hat der Syntax [
Die beiden Entitäten
Dabei müssen nur diejenigen Attribute mit dem Tabellennamen qualifiziert werden, die auch wirklich in beiden Tabellen vorkommen. Eine Angabe des Tabellennamen ist für die anderen Attribute zwar zulässig, jedoch nicht zwingend. AusdrückeOftmals ist es erwünscht oder sogar zwingend nötig, dass die Ergebnismenge nicht eins zu eins mit den Werten, die physisch auf der Datenbank abgespeichert sind, übereinstimmen sollen. Beispiele dafür wären Berechnungen, Verkettungen von Zeichenketten usw. SQL stellt für diesen Zweck Ausdrücke zur Verfügung:
In diesem Beispiel wird der Preis der selektierten Artikel mit dem Faktor 1.1 multipliziert ( Neben den Rechenoperatoren (+, -, *, /) können verschiedene Funktionen für die einzelnen Spalten verwendet werden. Eine Übersicht über die wichtigsten Funktionen befindet sich im "Aggregatsfunktionen". Spaltennamen und Ausdrücke dürfen in der Ausdrucksliste nach Belieben durchmischt werden, so lange sie mit einem Komma voneinander getrennt sind. Alias-Namen für Spalten und Ausdrücke
Oftmals möchte man in der Ergebnismenge nicht die gleichen Spaltennamen sehen, wie sie auf der Datenbank definiert sind. Dieses Bedürfnis kommt vor allem bei Ausdrücken zum tragen, da der Titel einer Ausdrucksspalte gleich lautet wie der Ausdruck selbst. Im obigen Beispiel würden die Spaltennamen in der Ergebnismenge beispielsweise "
SQL erlaubt es darum, dass für Spalten und Ausdrücke Alias-Namen vergeben werden können. Dazu wird das Schlüsselwort
Die Spalten in der Ergebnismenge würden in diesem Fall "
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Tabelle "cd" | ||
| id | bezeichnung | preis |
| 1 | Painkiller | 29.90 |
| 2 | The Number of the Beast | 15.90 |
| 3 | A Matter of Life and Death | 29.90 |
| 4 | Tyranny of Souls | 23.30 |
Auf den ersten Blick sind keine identischen Datensätze zu ermitteln. Selektiert man jedoch nur die Spalte preis aus dieser Tabelle, enthält diese Duplikate (Eintrag 1 und 3 haben den gleichen Preis). Ob diese Duplikate ausgegeben werden sollen oder nicht, kann mit dem Parameter ALL bzw. DISTINCT angegeben werden:
SELECT ALL preis FROM cd |
In diesem Falle lautet die Ergebnismenge {29.90, 15.90, 29.90, 23.30}. Es werden also auch Duplikate ausgegeben. Die Angabe des Parameters ALL ist optional. Wird nichts angegeben, so werden sämtliche Duplikate selektiert.
SELECT DISTINCT preis FROM cd |
Hier lautet die Ergebnismenge {29.90, 15.90, 23.30}, die Duplikate wurden unter Angabe des Parameters DISTINCT nicht selektiert.
FROM)Die Datenquelle einer Selektion ist in der Regel eine Tabelle oder eine View. Diese kann einfach mit ihrem Namen angegeben werden:
SELECT * FROM artikel |
Möchte man Spalten von mehreren Tabellen/Views selektieren, so müssen selbstverständlich sämtliche verwendeten Datenquellen angegeben werden.
SELECT vorname, nachname, bezeichnung, preis FROM person, artikel |
Wie bei den Spaltennamen und Ausdrücken können auch Datenquellen mit einem Alias-Namen selektiert werden:
SELECT * FROM TBL_ARTICLES AS artikel, TBL_PERSON AS person |
Dies ist vor allem in der WHERE-Klausel von Nutzen, da fortan mit den Alias-Namen auf die Spalten zugegriffen werden kann (siehe Folgeabschnitt).
WHERE-KlauselBisher wurde die Auswahl der Daten nur anhand der zurückgelieferten Spalten (horizontal) eingeschränkt. Es ist jedoch auch möglich, die Auswahl vertikal einzuschränken und somit nur die Datensätze auszuwählen, die bestimmten Kriterien genügen.
Dazu ein Beispiel:
SELECT preis FROM TBL_ARTICLES AS artikel WHERE artikel.preis = 29.90 |
Es werden sämtliche Artikel selektiert, bei denen der Preis genau dem Wert 29.90 entspricht. Hier sieht man auch gleich den Vorteil eines Alias-Namens für die Tabelle. Statt den Vergleich mit TBL_ARTICLES.preis zu formulieren, kann die besser lesbare Variante artikel.preis verwendet werden. Diese Qualifikation bei der WHERE-Klausel ist nur dann von Nöten, wenn mehrere Spalten mit dem Namen "preis" zurückgeliefert werden.
Selbstverständlich kann man nicht nur eine einzige Bedingung definieren. Es ist auch möglich, verschiedene Bedingungen mit den Operatoren AND und OR zu verknüpfen. Die Auswertungsreihenfolge kann mit runden Klammern beeinflusst werden.
Die folgende Tabelle listet mögliche Vergleichsoperatoren auf:
| Operator | Beschreibung |
= | Prüft auf Gleichheit |
!=, <> | Prüft auf Ungleichheit |
< | Prüft auf kleiner als |
> | Prüft auf grösser als |
<= | Prüft auf kleiner gleich |
>= | Prüft auf grösser gleich |
LIKE | Prüft, ob ein Wert einem Muster entspricht (Zeichenketten) |
BETWEEN | Prüft, ob ein Wert im angegebenen Bereich liegt |
IN | Prüft, ob der Wert in der angegebenen Wertmenge vorhanden ist |
IS [NOT] NULL | Prüft, ob ein Wert NULL bzw. nicht NULL entspricht |
| |
|
Dazu einige Beispiele:
SELECT lagerbestand, preis FROM artikel WHERE lagerbestand >= 1 AND preis < 30 |
In diesem Beispiel werden sämtliche Artikel selektiert, bei denen der Lagerbestand grösser oder gleich 1 und der Preis kleiner als 30 ist.
SELECT bezeichnung, lagerbestand, preis FROM artikel WHERE bezeichnung LIKE '%bier%' AND lagerbestand BETWEEN 100 AND 200 |
Es werden sämtliche Artikel selektiert, bei denen die Bezeichnung die Zeichenkette "bier" enthält. Dazu wird der LIKE-Operator mit den Wildcards % verwendet. Diese steht für keines, eines oder beliebig viele Zeichen. Weiter existiert die Wildcard _ (underscore), welche stellvertretend für kein oder ein Zeichen steht. Zudem muss der Lagerbestand der selektierten Artikel zwischen 100 und 200 liegen.
SELECT bezeichnung, lagerbestand, preis
FROM artikel
WHERE bezeichnung IN ('Korn', 'Bier', 'Schnaps', 'Wein', 'Aspirin')
|
In diesem Beispiel werden sämtliche Artikel selektiert, deren Bezeichnung einem Element in der definierten Menge ('Korn', 'Bier', usw.) entspricht. Es ist auch möglich, zu diesem Zweck eine Unterabfrage zu definieren:
SELECT bezeichnung, lagerbestand, preis FROM artikel WHERE bezeichnung IN (SELECT bezeichnung FROM Trinkutensilien) |
In diesem Fall muss die Artikelbezeichnung einem Wert (genauer der Bezeichnung) eines Datensatzes aus der Tabelle Trinkutensilien entsprechen.
Die Operatoren LIKE, BETWEEN und IN (wie auch IS NULL, IS NOT NULL) können mit dem NOT-Operator negiert werden:
SELECT bezeichnung, lagerbestand, preis FROM artikel WHERE bezeichnung NOT LIKE '%bier%' AND lagerbestand NOT BETWEEN 200 AND 500 AND preis NOT IN (SELECT preis FROM wucherei) |
Für Operatoren wie >, <, >= usw. ist die Verwendung von NOT nicht zulässig bzw. nicht sinnvoll, da es für diese jeweils ein entsprechendes Pendant gibt (!=, <> und =, > und <, >= und <=).
GROUP BY)SQL erlaubt es, Zeilen in verschiedenen Gruppen zusammenzufassen und dann nur eine dieser Zeilen zurückzuliefern. Um eine Ergebnismenge nach bestimmten Kriterien zu gruppieren, bestehen folgende Möglichkeiten:
GROUP BY"-Klausel angegeben.
Eine Spalte muss sich entweder in der "GROUP BY"-Klausel befinden oder es muss eine Aggregatsfunktion auf sie angewendet werden. Ansonsten ist das Ergebnis der Gruppierung nicht definiert.
Zum besseren Verständnis ein Beispiel. Wir gehen von einer Tabelle mit Personen aus (die Spalte mit dem Alter der Person wurde absichtlich mit dem englischen Wort "age" benannt, da das deutsche Wort "Alter" ein SQL-Schlüsselwort darstellt und somit nicht für Spaltennamen verwendet werden kann):
Tabelle "person" | |||
| id | vorname | nachname | age |
| 1 | Alice | Meier | 87 |
| 2 | Barbara | Huber | 42 |
| 3 | Claudio | Meier | 12 |
| 4 | Daniel | Meier | 65 |
| 5 | Elvira | Huber | 33 |
| 6 | Fabian | Birrer | 56 |
Wir erstellen eine Selektion, um das älteste Familienmitglied zu ermitteln (wir gehen davon aus, dass der gleiche Nachname eine Verwandschaft darstellt). Dazu soll nach der Spalte nachname gruppiert werden. Die Selektion dazu sieht folgendermassen aus:
SELECT nachname, MAX(age) AS age FROM person GROUP BY nachname |
In der ersten Zeile wird mit MAX(age) angegeben, dass jeweils der grösste Alterswert ausgegeben wird. Dieser Ausdruck macht erst im Zusammenhang mit der Gruppierung in Zeile 3 Sinn. Dabei werden sämtliche Datensätze mit dem gleichen Nachnamen zu einer Gruppe hinzugefügt. Neben dem Nachnamen wird auch das Alter selektiert. Wir erhalten als Ergebnismenge somit eine Liste aller Nachnamen mit dem jeweiligen Höchstwert aus der Spalte age zu diesem Nachnamen. Die Ergebnismenge sieht dann folgendermassen aus:
| nachname | age |
| Birrer | 56 |
| Huber | 42 |
| Meier | 87 |
HAVING-Klausel
Die HAVING-Klausel folgt der gleichen Syntax wie die WHERE-Klausel. Beide Klauseln haben die Funktion, die zu selektierende Datenmenge anhand bestimmter Kriterien einzuschränken. Im Unterschied zur WHERE-Klausel schränkt die HAVING-Klausel die Datenmenge nach einer vollzogenen Gruppierung ein.
WHERE schränkt die Datenmenge einGROUP BY) reduziert die Ergebnismenge anhand von Gruppierungsattributen und AggregatsfunktionenHAVING schränkt die gruppiete Ergebnismenge weiter nach bestimmten Kriterien einDazu betrachten wir am besten noch einmal das Beispiel aus dem vorhergehenden Abschnitt. Wir möchten diese Ergebnismenge nun nach Familiennamen einschränken, deren ältestes Familienmitglied das 50 Altersjahr schon hinter sich gelassen hat. Die Abfrage dazu sieht dann so aus:
SELECT nachname, MAX(age) AS age FROM person GROUP BY nachname HAVING age > 50 |
Zu beachten ist in diesem Fall, dass für den Ausdruck MAX(age) ein Alias-Name definiert wurde (age). Der Vergleich in der HAVING-Klausel erfolgt dann anhand dieses Alias-Namens. In diesem Fall wird die Ergebnismenge um den Eintrag mit dem Nachnamen "Huber" und dem entsprechenden Alter 42 reduziert, was folgende Ergebnismenge zur Folge hat:
| nachname | age |
| Huber | 42 |
| Meier | 87 |
Nach Möglichkeit sollten so viele Kriterien wie möglich bereits in der WHERE-Klausel aufgeführt werden. Dies reduziert die Datenmenge, die das Datenbanksystem anschliessend gruppieren muss. In diesem Beispiel hätte die Bedingung [age > 50] auch schon in der WHERE-Klausel definiert werden können, die Ergebnismenge wäre die Gleiche geblieben.
ORDER BY)
Der SELECT-Befehl stellt die "ORDER BY"-Klausel zur Verfügung, um damit eine bestimmte Sortierung in der Ergebnismenge zu erreichen. Es können beliebig viele Spalten angegeben werden, welche wahlweise aufsteigend (ASC) oder absteigend (DESC) sortiert werden können. Betrachten wir dazu wieder das Beispiel mit der Personen-Tabelle:
Tabelle "person" | |||
| id | vorname | nachname | age |
| 1 | Alice | Meier | 87 |
| 2 | Barbara | Huber | 42 |
| 3 | Claudio | Meier | 12 |
| 4 | Daniel | Meier | 65 |
| 5 | Elvira | Huber | 33 |
| 6 | Fabian | Birrer | 56 |
Als erstes Kriterium soll nach dem Nachnamen gruppiert werden. Da aber der gleiche Nachname bei verschiedenen Personen eingetragen ist, lässt sich die Sortierung weiter verfeinern. Die Personen sollen zusätzlich nach ihrem Alter sortiert werden. Der SELECT-Befehl dafür sieht dann folgendermassen aus:
SELECT * FROM person ORDER BY nachname ASC, age DESC |
Die "ORDER BY"-Klausel besagt in diesem Fall, dass die Spalte nachname aufsteigend (d.h. anhand des Alphabets, von A bis Z) sortiert werden soll. Weiter wird absteigend nach der Spalte age sortiert, sodass innerhalb der gleichen Familie zuerst die Familienmitglieder höheren Alters aufgelistet werden. Das Ergebnis dieser Abfrage präsentiert sich dann folgendermassen:
Tabelle "person" | |||
| id | vorname | nachname | age |
| 6 | Fabian | Birrer | 56 |
| 2 | Barbara | Huber | 42 |
| 5 | Elvira | Huber | 33 |
| 1 | Alice | Meier | 87 |
| 4 | Daniel | Meier | 65 |
| 3 | Claudio | Meier | 12 |
Die Angabe der Sortierreihenfolge ASC ist optional. Wird keine Sortierreihenfolge angegeben, so wird automatisch ASC angenommen. Somit ist nur die explizite Angabe von DESC erforderlich.
JOIN)Nicht immer befinden sich alle relevanten Daten für eine Abfrage in einer einzigen Tabelle. Eine Abfrage muss somit über mehrere Tabellen hinweg erfolgen. Um die Daten der verschiedenen Tabellen aber dennoch zu einer einzigen Ergebnismenge zusammenfügen zu können, müssen die Tabellen anhand bestimmter Kriterien verknüpft werden. Zu diesem Zweck eignen sich Fremdschlüsselfelder, Datensätze verschiedener Tabellen können über Fremdschlüssel miteinander "verbunden" werden.
In den folgenden Abschnitten wird von der folgenden Tabellenstruktur ausgegangen:
Tabelle "auto" | |||
| id | marke | modell | fk_besitzer |
| 1 | Opel | Kadett | 2 |
| 2 | Audi | A3 | 3 |
| 3 | VW | Golf | 2 |
| 4 | BMW | 3er | NULL |
| 5 | Fiat | Panda | 5 |
Tabelle "person" | |||
| id | name | ||
| 1 | Hans Meier | ||
| 2 | Sepp Birrer | ||
| 3 | Alice Huber | ||
| 4 | Barbara Kunz | ||
| 5 | Martin Duss | ||
Die Assoziation zwischen den einzelnen Datensätzen ist in diesem Falle durch den Fremdschlüssel fk_besitzer der Tabelle auto auf das Feld id der Tabelle person zu erstellen.
Die einfachste Möglichkeit, diese beiden Tabellen miteinander zu verknüpfen, ist die Angabe beider Tabellennamen in der FROM-Klausel:
SELECT * FROM autos, person |
Auf die Angabe der Ergebnismenge möchte ich in diesem Fall verzichten, da diese Selektion nichts anderes macht, als ein karthesisches Produkt zwischen den Datensätzen dieser beiden Tabellen herzustellen. Somit wird jeder Datensatz aus der Tabelle autos mit jedem Datensatz aus der Tabelle person angezeigt. Diese Ergebnismenge hat kaum eine praktische Verwendung.
Erinnern wir uns an die WHERE-Klausel. Diese erlaubt es nicht nur, Datensätze anhand bestimmter Bedingungen zu selektieren, wir können damit auch Datensätze "zuordnen". Betrachten wir dazu folgendes Beispiel:
SELECT auto.marke, auto.modell, person.name AS besitzer FROM auto, person WHERE auto.fk_besitzer = person.id |
Die WHERE-Klausel besagt, dass die Datensätze der Tabelle auto mit den Datensätzen der Tabelle person assoziiert werden sollen. Die Bedingung dafür lautet, dass das Feld auto.fk_besitzer jeweils dem Feld person.id entsprechen muss. Wir erhalten somit als Ergebnis eine Zuordnung der Autos zu ihren Besitzern:
| marke | modell | besitzer |
| Opel | Kadett | Sepp Birrer |
| Audi | A3 | Alice Huber |
| VW | Golf | Sepp Birrer |
| Fiat | Panda | Martin Duss |
Diese Abfrage lässt sich auch noch anders formulieren, mit einem sog. JOIN. Die Syntax dafür lautet folgendermassen:
SELECT Spalten FROM Datenquelle INNER JOIN Datenquelle ON Bedingung |
Übertragen auf unser Beispiel ergibt dies folgenden SQL-Befehl:
SELECT auto.marke, auto.modell, person.name AS besitzer FROM auto INNER JOIN person ON auto.fk_besitzer = person.id |
Wichtig ist, dass die FROM-Klausel nur eine einzige Tabelle umfasst (Tabelle auto). Die weiteren Tabellen werden dann mit der JOIN-Klausel assoziiert. Die Ergebnismenge bleibt die Gleiche.
Dieser JOIN kann über beliebig viele Tabellen weiter gezogen werden. So könnte man z.B. Datensätze der Tabelle ort in die Abfrage miteinbeziehen (nehmen wir an, die Tabelle person enthält ein Feld namens fk_ort, das auf die Tabelle ort verweist):
SELECT auto.marke, auto.modell, person.name AS besitzer, ort.name AS ort FROM auto INNER JOIN person ON auto.fk_besitzer = person.id INNER JOIN ort ON person.fk_ort = ort.id |
Neben dem INNER JOIN gibt es noch weitere Möglichkeiten für JOINs. Erwähnenswert sind folgende JOINs:
LEFT JOIN
RIGHT JOIN
Der SQL-Standard stellt folgende Aggregatsfunktionen zur Verfügung:
| Funktion | Beschreibung |
COUNT() | Liefert die Anzahl der Werte in der Ergebnismenge einer SELECT-Abfrage bzw. einer Gruppierung. |
COUNT(DISTINCT) | Liefert die Anzahl der unterschiedlichen Werte in der Ergebnismenge einer SELECT-Abfrage bzw. einer Gruppierung. |
AVG() | Liefert den Durchschnittswert eines Datenfeldes einer SELECT-Abfrage bzw. einer Gruppierung. |
MIN() | Liefert den kleinsten Wert eines Datenfeldes einer SELECT-Abfrage bzw. einer Gruppierung. |
MAX() | Liefert den grössten Wert eines Datenfeldes einer SELECT-Abfrage bzw. einer Gruppierung. |
SUM() | Liefert die Summe der Werte eines Datenfeldes einer SELECT-Abfrage bzw. einer Gruppierung. |
|
|
|