SELECT
-Statement
Eine der häufigsten Aufgaben bei der Arbeit mit einer Datenbank ist das Abrufen von Daten aus Tabellen mittels des SELECT
-Statements.
Die SELECT
-Anweisung ist eine der komplexesten Anweisungen in SQL. PostgreSQL zeichnet sich sogar durch besonders viele und nützlich Zusatzfeatures aus. Die Anweisung enthält viele Klauseln, die Sie zur Erstellung von einfachen bis komplexen Abfragen verwenden können.
Aufgrund ihrer Komplexität werden wir sie in mehrere kürzere und leicht verständliche „Häppchen“ aufteilen. So können Sie sich schneller und leichter mit den einzelnen Klauseln vertraut machen.
Die SELECT
-Anweisung enthält die folgenden Klauseln:
- Fassen sie Zeilenduplikate mit dem
DISTINCT
-Operator zusammen. - Sortieren Sie Zeilen mit der
ORDER
BY
-Klausel. - Filtern Sie Zeilen mit der
WHERE
-Klausel. - Wählen Sie eine Teilmenge von aufeinander folgenden Zeilen aus einer Tabelle mithilfe der
LIMIT
-Klausel aus. - Gruppieren Sie Zeilen mithilfe der
GROUP
BY
-Klausel. - Filtern Sie Gruppen mithilfe der
HAVING
-Klausel. - Verknüpfen Sie mehrere Tabellen mithilfe von Joins mit
INNER
JOIN
,LEFT
JOIN
,FULL
OUTER
JOIN
,CROSS
JOIN
-Klauseln. - Führen Sie Mengenoperationen mithilfe von
UNION
,INTERSECT
undEXCEPT
aus.
Im ersten Teilabschnitt konzentrieren wir uns vorerst auf die SELECT
– und FROM
-Klauseln.
Syntax des SELECT
-Statements
Beginnen wir mit der Grundform der SELECT
-Anweisung, mit der Daten aus einer einzelnen Tabelle abgerufen werden.
Die folgende Abbildung zeigt die Syntax der SELECT
-Anweisung:
1 2 3 |
SELECT select_list FROM table_name; |
- Zunächst geben Sie eine Auswahlliste an, die eine Spalte oder eine Liste von Spalten in einer Tabelle sein kann, aus der Sie Daten abrufen möchten. Wenn Sie eine Liste von Spalten angeben, müssen Sie ein Komma zwischen zwei Spalten setzen, um sie zu trennen. Wenn Sie Daten aus allen Spalten der Tabelle auswählen möchten, können Sie eine Sternchen-Kurzschreibweise (
*
) verwenden, anstatt alle Spaltennamen einzeln anzugeben. Die Auswahlliste kann auch Ausdrücke oder Literalwerte enthalten. - Geben Sie anschließend den Namen der Tabelle an, aus der Sie Daten abfragen möchten, und zwar nach dem
FROM
-Schlüsselwort. - Mithilfe von Einrückungen können Sie Ihren Quellcode lesbar formatieren. Die Einrückungen, Zeilenumbrüche etc. sind dem SQL-Interpreter egal. Sie werden von ihm ignoriert.
- Ein SQL-Statement endet mit einem Semikolon. Genau genommen, werden SQL-Statements durch je ein Semikolon voneinander getrennt.
Tatsächlich ist die FROM
-Klausel ist optional. Wenn Sie keine Daten aus einer Tabelle abfragen, können Sie die FROM
-Klausel in der SELECT
-Anweisung weglassen. PostgreSQL wertet die FROM
-Klausel vor der SELECT
-Klausel in der SELECT
-Anweisung.
Beachten Sie, dass die SQL-Schlüsselwörter nicht zwischen Groß- und Kleinschreibung unterscheiden. Das bedeutet, dass SELECT
gleichbedeutend mit select
oder Select
ist. Gemäß Konvention werde ich hier alle SQL-Schlüsselwörter in Großbuchstaben schreiben, um die Abfragen leichter lesbar zu gestalten.
SELECT
-Beispiele
Abfrage der Daten einer einzigen Spalte
Die folgende Abfrage zeigt Ihnen, wie Sie alle Vornamen (first_name
) der Kunden aus der Tabelle customer
abfragen.
1 2 3 |
SELECT first_name FROM customer; |
Hier ist ein Auszug des Ergebnisses:
+----------+ |first_name| +----------+ |Jared | |Mary | |Patricia | |Linda | |Barbara | |Elizabeth | ... (insgesamt 599 Datensätze)
Beachten Sie, dass wir am Ende der SELECT
-Anweisung ein Semikolon notiert haben. Das Semikolon ist kein Teil der SQL-Anweisung, sondern dient PostgreSQL als Signal, dass eine SQL-Anweisung abgeschlossen ist. Außerdem werden Semikolons verwendet, um zwei oder mehr SQL-Anweisungen voneinander zu trennen. Haben Sie nur ein einziges SQL-Statement in Ihrem Abfrage-Editor, z.B. pgAdmin 4, so stört es nicht, wenn Sie das Semikolon vergessen haben. Sie sollten es sich jedoch strikt angewöhnen, das Semikolon zum Abschluss eines Statements zu setzen. Das vermeidet Fehler, wenn Sie mehr als nur ein Statement in Ihrem Editor bearbeiten.
Abfrage mit mehreren Spalten
Das folgende Statement fragt drei Spalten der Tabelle customer
ab.
1 2 3 4 5 |
SELECT first_name , last_name , email FROM customer; |
+-----------+------------+----------------------------------------+ |first_name |last_name |email | +-----------+------------+----------------------------------------+ |Jared |Ely |jared.ely@sakilacustomer.org | |Mary |Smith |mary.smith@sakilacustomer.org | |Patricia |Johnson |patricia.johnson@sakilacustomer.org | |Linda |Williams |linda.williams@sakilacustomer.org | |Barbara |Jones |barbara.jones@sakilacustomer.org | ... (599 Datensätze insgesamt)
Die Ausgabe der Spalten entspricht deren Reihenfolge im SELECT
-Statement. Wollen Sie sie ändern, ändern Sie einfach deren Reihenfolge in der Spaltenauflistung hinter SELECT
.
Abfrage der Daten aller Spalten
Wollen Sie alle Spalten einer Tabelle ausgeben, können Sie diese mühsam alle einzeln notieren. Es geht aber auch bequemer. SQL bietet den *
-Operator, ein Hüllenoperator. Er repräsentiert alle Spalten.
1 2 |
SELECT * FROM customer; |
+-----------+--------+----------+---------+-----------------------------------+----------+----------+-----------+--------------------------+------+ |customer_id|store_id|first_name|last_name|email |address_id|activebool|create_date|last_update |active| +-----------+--------+----------+---------+-----------------------------------+----------+----------+-----------+--------------------------+------+ |524 |1 |Jared |Ely |jared.ely@sakilacustomer.org |530 |true |2006-02-14 |2013-05-26 14:49:45.738000|1 | |1 |1 |Mary |Smith |mary.smith@sakilacustomer.org |5 |true |2006-02-14 |2013-05-26 14:49:45.738000|1 | |2 |1 |Patricia |Johnson |patricia.johnson@sakilacustomer.org|6 |true |2006-02-14 |2013-05-26 14:49:45.738000|1 | |3 |1 |Linda |Williams |linda.williams@sakilacustomer.org |7 |true |2006-02-14 |2013-05-26 14:49:45.738000|1 | |4 |2 |Barbara |Jones |barbara.jones@sakilacustomer.org |8 |true |2006-02-14 |2013-05-26 14:49:45.738000|1 | |5 |1 |Elizabeth |Brown |elizabeth.brown@sakilacustomer.org |9 |true |2006-02-14 |2013-05-26 14:49:45.738000|1 | ... (599 Datensätze insgesamt)
Die Verwendung des Sternchens dex *
-Symbols ist bequem, gilt jedoch als schlechte Praxis, wenn Sie SQL-Anweisungen in den Anwendungscode einbetten, wie beispielsweise in Python, Java oder PHP. Das hat die folgenden Gründe:
- Datenbankleistung: Angenommen, Sie haben eine Tabelle mit vielen Spalten und umfangreichen Daten. Die
SELECT
-Anweisung mit der Sternchen-Kurzschreibweise wählt Daten aus allen Spalten der Tabelle aus und ruft möglicherweise mehr Daten ab, als für die Anwendung erforderlich sind. - Anwendungsleistung: Das Abrufen unnötiger Daten aus der Datenbank erhöht den Datenverkehr zwischen dem Datenbankserver und dem Anwendungsserver. Dies kann zu langsameren Antwortzeiten und einer geringeren Skalierbarkeit Ihrer Anwendungen führen.
Aus diesen Gründen wird empfohlen, die Spaltennamen in der SELECT
-Klausel nach Möglichkeit explizit anzugeben. Dadurch wird sichergestellt, dass nur die erforderlichen Daten aus der Datenbank abgerufen werden, was zu effizienteren und optimierten Abfragen beiträgt.
Die Sternchen-Kurzschreibweise sollte ausschließlich für Ad-hoc-Abfragen genutzt werden, um sich einen schnellen Überblick über die Daten aus der Datenbank zu machen.
Abfrage mit Ausdrücken
Ein Ausdruck ist eine Operation oder auch ein Funktionsaufruf, etwas, was zu einem Datenwert evaluiert werden kann.
1 2 3 |
SELECT last_name || ', ' || first_name FROM customer; |
Das doppelte Pipe-Symbol ist in PostgreSQL ein Operator zum Verknüpfen von Zeichenketten. Diese werden literal in einfache Anführungszeichen eingeschlossen.
Bei der Ausgabe fällt auf, das Nach- und Vorname zwar, mit einem Komma und einem Leerzeichen voneinander getrennt, verknüpft wurden, jedoch eine vernünftige Spaltenüberschrift fehlt. – Das Problem werden wir im kommenden Abschnitt lösen.
Hier ist ein Auszug des Ergebnisses:
+-----------------+ |?column? | +-----------------+ |Ely, Jared | |Smith, Mary | |Johnson, Patricia| |Williams, Linda | |Jones, Barbara | ... (insgesamt 599 Datensätze)
Aliasbezeichner für Spalten
Bei der letzten Abfrage des vorangegangenen Abschnitts wurde ?column?
als Spaltenbezeichner ausgegeben. Der Grund dafür ist einfach. Wir haben natürlich Spalten für die Ausgabe ausgewählt, diese jedoch verknüpft, sodass aus je zwei Spalten pro Datensatz ein neuer Wert aus ihnen generiert wurde. Dem Verknüpfungsausdruck haben wir jedoch keinen Namen gegeben.
Um einer Spalte temporär einen Namen zu geben oder einen vorhandenen Namen umzubenennen, gibt es Aliasbezeichner.
Ein Aliasbezeichner wird direkt hinter dem Ausdruck oder der umzubenennender Spalte notiert. Um den Code einfacher lesbar zu gestalten, können wir optional ein AS
vor den Aliasbezeichner schreiben.
1 2 3 |
SELECT last_name || ', ' || first_name FROM customer; |
+-----------------+ |fullname | +-----------------+ |Ely, Jared | |Smith, Mary | |Johnson, Patricia| |Williams, Linda | |Jones, Barbara | ... (599 Datensätze insgesamt)
Als gültige Zeichen für einen Alias können Sie alle Zeichen des Grundalphabets und den Unterstrich verwenden. Auf Leerzeichen sollten Sie, da es das Trennzeichen für Token ist, verzichten. Sollten Sie es sich dennoch nicht verkneifen können, ein Leerzeichen zu verwenden, müssen Sie den Bezeichner in doppelte Anführungszeichen einschließen. Das ist jedoch ein so schlechter Stil, dass ich deshalb sogar auf die Demonstration dieses Vorgehens verzichte.
SELECT
ohne FROM
Die FROM
-Klausel des SELECT
-Statements ist optional. Sie wird benötigt, wenn wir Tabellen oder Tabellenausdrücke, die wir später noch behandeln werden, als Quelle für unsere Abfragen
nutzen.
Funktionen, die nur einen Wert liefern, wie beispielsweise now()
, können wir direkt hinter SELECT
aufführen, ohne eine FROM
-Klausel.
1 |
SELECT now(); |
+---------------------------------+ |now | +---------------------------------+ |2024-12-27 12:55:47.156784 +00:00| +---------------------------------+
now()
gibt einen sogenannten TIMESTAMP
zurück. Das ist ein Datumswert inklusive Uhrzeit. Die Angabe +01:00
zeigt an, dass die Abfrage in einer Zeitzone mit einer Stunde Verschiebung gegenüber UTC (Universal Time Coordinated) getätigt wurde.
Statt einer Funktion können wir auch andere Ausdrücke einsetzen. Zum Beispiel können wir PostgreSQL als eine Art Taschenrechner verwenden.
1 |
SELECT 2 + 2 * 20 AS die_antwort; |
Beachten Sie, dass PostgreSQL die arithmetische Regel „Punkt-vor-Strichrechnung“ korrekt berücksichtigt.
+-----------+ |die_antwort| +-----------+ |42 | +-----------+
Sortieren mit ORDER
BY
Wenn Sie Daten aus einer Tabelle abfragen, gibt die SELECT
-Anweisung Zeilen in einer nicht festgelegten Reihenfolge zurück. Um die Zeilen des Ergebnissatzes zu sortieren, und so eine vorgegebene Ordnung zu erzielen, verwenden Sie die ORDER
BY
-Klausel in der SELECT
-Anweisung.
Die ORDER
BY
-Klausel ermöglicht es Ihnen, die von SELECT
zurückgegebenen Zeilen, basierend auf einem Sortierausdruck, in aufsteigender oder absteigender Reihenfolge zu sortieren.
Im Folgenden wird die Syntax der ORDER
BY
-Klausel veranschaulicht:
1 2 3 4 5 6 7 |
SELECT select_list FROM table_name ORDER BY sort_expression1 [ASC|DESC] , sort_expression2 [ASC|DESC]; |
- Geben Sie zunächst einen Sortierausdruck an, der eine Spalte oder ein Ausdruck sein kann, nach dem Sie sortieren möchten, und zwar nach den Schlüsselwörtern
ORDER
BY
. Wenn Sie die Ergebnismenge nach mehreren Spalten oder Ausdrücken sortieren möchten, müssen Sie ein Komma zwischen zwei Spalten oder Ausdrücken einfügen, um sie voneinander zu trennen. - Zweitens verwenden Sie die Option
ASC
, um Zeilen in aufsteigender Reihenfolge zu sortieren, und die OptionDESC
, um Zeilen in absteigender Reihenfolge zu sortieren. Ohne Angabe verwendetORDER
BY
standardmäßigASC
.
Sortierbeispiele
Sortieren nach einer Spalte
Das folgende Statement verwendet die ORDER
BY
-Klausel, um Kundendatensätze nach ihren Vornamen aufsteigend zu sortieren. Der Zusatz ASC
hätte hierbei entfallen dürfen, weil er die Standardsortierung (aufsteigend) markiert.
1 2 3 4 5 6 7 |
SELECT first_name , last_name FROM customer ORDER BY first_name ASC; |
+----------+---------+ |first_name|last_name| +----------+---------+ |Aaron |Selby | |Adam |Gooch | |Adrian |Clary | |Agnes |Bishop | |Alan |Kahn | ... (599 Datensätze insgesamt)
Hinweis: Eine Sortierspalte muss nicht zwingend notwendigerweise in den auszugebenden Spalten benannt sein. Es kann und darf ebenso nach einer Spalte sortiert werden, die nicht ausgegeben wird.
Sortieren nach mehreren Spalten
Die folgende Abfrage sortiert die Ausgabe zuerst absteigend nach der Filialnummer. Innerhalb gleicher Filialnummern wird erst nach Nachnamen und dann nach Vornamen sortiert.
1 2 3 4 5 6 7 8 9 |
SELECT first_name , last_name FROM customer ORDER BY store_id DESC , last_name , first_name; |
+----------+---------+ |first_name|last_name| +----------+---------+ |Kathleen |Adams | |Shirley |Allen | |Charlene |Alvarez | |Lisa |Anderson | |Ida |Andrews | ... (599 Datensätze insgesamt)
Sie können sich leicht von der vorrangigen Sortierung nach der Filialnummer überzeugen. Blenden Sie sie einfach ein, indem Sie sie hinter SELECT
, egal an welcher Position, aufführen.
Sortieren nach Ausdrücken
Sortierkriterien sind nicht auf die originären Werte irgendwelcher Spalten beschränkt. Sie können ebenso Ausdrücke verwenden. So können Sie die Kunden beispielsweise nach absteigender Länge Ihrer vollen Namen und innerhalb gleicher Längen erst nach Nach- und dann nach Vornamen, jeweils aufsteigend, sortieren.
1 2 3 4 5 6 7 8 9 |
SELECT first_name , last_name FROM customer ORDER BY length(last_name || first_name) DESC , last_name , first_name; |
+----------+------------+ |first_name|last_name | +----------+------------+ |Mitchell |Westmoreland| |Jonathan |Scarborough | |William |Satterfield | |Catherine |Campbell | |Nelson |Christenson | ... (599 Datensätze insgesamt)
Sortieren mit NULL
-Werten
In der Welt der Datenbanken ist NULL
ein Marker, der angibt, dass Daten fehlen oder zum Zeitpunkt der Aufzeichnung unbekannt sind.
Wenn Sie Zeilen sortieren, die NULL
enthalten, können Sie die Reihenfolge von NULL
mit anderen Nicht-NULL
-Werten angeben, indem Sie die Option NULLS
FIRST
oder NULLS
LAST
der ORDER
BY
-Klausel verwenden:
1 2 |
ORDER BY sort_expression [ASC|DESC] [NULLS FIRST|NULLS LAST] |
PostgreSQL verwendet als Standard, wenn nichts angegeben ist, die Option NULLS
LAST
. Hierin können sich unterschiedliche Datenbankmanagementsysteme voneinander unterstützen. Auch unterstützen nicht alle die Klauseln NULLS
FIRST
und NULLS
LAST
. Wenn dem der Fall sein sollte, müssen Sie sich etwas anderes einfallen lassen. Workarounds sind jedoch leicht zu finden.
Wir betrachten die Tabelle address
und geben die Datenfelder postal_code
und address
aus. Die Sortierung soll aufsteigend nach Postleitzahlen erfolgen. Mögliche NULL
-Werte sollen zuerst ausgegeben werden.
1 2 3 4 5 6 |
SELECT address , postal_code FROM address ORDER BY postal_code NULLS FIRST; |
+--------------------+-----------+ |address |postal_code| +--------------------+-----------+ |1411 Lillydale Drive|null | |47 MySakila Drive |null | |28 MySQL Boulevard |null | |23 Workhaven Lane |null | |1542 Tarlac Parkway |1027 | |1190 0 Place |10417 | ... (603 Datensätze insgesamt)
Dubletten vermeiden mit SELECT
DISTINCT
DISTINCT
entfernt Dubletten aus der Ergebnismenge eines SELECT
-Kommandos. Die DISTINCT
-Klausel wird direkt hinter SELECT
notiert. Dahinter werden dann, wie gehabt, die auszugebenden Spalten angegeben.
1 2 |
SELECT DISTINCT column(s)|* FROM table_name; |
Ermitteln der verschiedenen Film-Ratings
Betrachten Sie die Tabelle film
. Jeder Film ist mittels des Attributs rating
mit einer (amerikanischen) Alterfreigabe ausgewiesen. Diese kommen in der Tabelle wiederholt vor.
Wir wollen ermitteln, welche Altersfreigaben es gibt bzw. in der Film-Tabelle verwendet werden. Dazu müssen wir Mehrfachnennungen auf jeweils eine einzige Nennung komprimieren.
1 2 3 4 5 |
SELECT DISTINCT rating FROM film ORDER BY rating; |
+------+ |rating| +------+ |G | |PG | |PG-13 | |R | |NC-17 | +------+
Wundern Sie sich nicht, wenn die Sortierung im ersten Moment nicht Ihren Erwartungen entspricht. Wenn dem so ist, haben Sie vermutlich angenommen, die Altersfreigaben würden alphabetisch sortiert. Tatsächlich handelt es sich bei Ihnen um einen eigens in der Datenbank implementierten Datentypen. Der Typ heißt mpaa_rating
und ist ein sogenannter Enumerationsdatentyp. Das bedeutet, seine möglichen Werte wurden als Aufzählung definiert. Die Reihenfolge der aufgezählten Werte ist dabei entscheidend für deren Ordnung. – Wir werden uns später noch (intensiver) mit selbstdefinierten Datentypen befassen.
Zum Verständnis dieser Kategorisierung habe ich die Bedeutung der Altersfreigaben hier einmal für Sie zusammengetragen.
- G – General Audiences: Alle Altersgruppen zugelassen. Nichts, was Eltern daran hindern würde, sich einen Film gemeinsam mit ihren Kindern anzusehen.
- PG – Parental Guidance Suggested: Einige Inhalte der Filme sind eventuell nicht für Kinder geeignet. Es wird empfohlen, die Filme nur unter elterlicher Aufsicht anzusehen.
- PG-13Parents Strongly Cautioned: Einige Inhalte sind für Kinder unter 13 Jahren nicht geeignet. Es wird dringend empfohlen, diese Filme nur unter elterlicher Aufsicht anzusehen.
- R – Restricted: Jugendliche unter 17 Jahren dürfen diese Filme nur in Begleitung der Eltern oder eines Vormundes ausleihen/ansehen.
- NC-17 Adults Only: Erwachsenenfilme.