Einfache SQL-Abfragen

  • Beitrags-Autor:
  • Beitrags-Kategorie:Abfragen

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 und EXCEPT 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:

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

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.

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

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

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.

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

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

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:

  • 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 Option DESC, um Zeilen in absteigender Reihenfolge zu sortieren. Ohne Angabe verwendet ORDER BY standardmäßig ASC.

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.

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

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

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

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.

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

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.

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