Subselects

Subselects bilden eine effiziente und komfortable Möglichkeit, komplexere Abfragen in ein einziges SQL-Statement zu verpacken. Bestimmte Abfragen sind ohne Subselects nur sehr schwierig oder gar nicht möglich. Die Implementierung von geschäftskritischen Anwendungen ist also ohne Subselects als eher schwierig einzustufen.

Das klassische Beispiel, um Lesern Subselects näher zu bringen ist die Abfrage aller Datensätze, die kleiner sind als der Mittelwert der selbigen Datensätze:

test=# SELECT * 
	FROM t_produkt 
	WHERE price < (SELECT avg(price) FROM t_produkt) 
	ORDER BY price;
 id |     name     | price
----+--------------+-------
  5 | Apfel        |  0.45
  1 | Semmel       |     1
  2 | Käsestangerl |     2
  4 | Rose         |  2.16
(4 rows)

Die Berechnung des durchschnittlichen Preises erfolgt direkt als Teil der WHERE-Clause. Das hat den Vorteil, dass wir die Frage in einem einzigen Statement beantworten können. Alternativ könnte man auch wie folgt vorgehen aber das hätte einige entscheidende Nachteile:

test=# SELECT avg(price) FROM t_produkt;
         avg
---------------------
 44.3220000000000000
(1 row)

test=# SELECT * FROM t_produkt WHERE price < 44.322;
 id |     name     | price
----+--------------+-------
  1 | Semmel       |     1
  2 | Käsestangerl |     2
  4 | Rose         |  2.16
  5 | Apfel        |  0.45
(4 rows)

Das Ergebnis entspricht dem zuvor gezeigten Ergebnis - es ist aber keineswegs sichergestellt, dass es in alle Fällen korrekt ist. Wenn ein gleichzeitig agierender Benutzer während dem ersten SQL-Statement einen Datensatz einfügt, liefert unsere Mittelwertberechnung immer noch das selbe Ergebnis - möglicherweise liefert das zweite SELECT-Statement jedoch eine Datenzeile mehr. Das Ergebnis wäre somit verzerrt. Ein MySQL-Programmierer hat mir einmal allen Ernstes und bei vollem Bewusstsein erzählt, dass Näherungswerte in der Praxis völlig ausreichen. Die Frage, die sich stellt, ist, ob Sie mit Näherungswerten für Ihren Kontostand in der Praxis auch zufrieden wären. Diese kleine und tragischerweise wahre Begebenheit zeigt, wie wichtig die Konsistenz von Ergebnissen ist und dass man nie oft genug auf die Risiken bei der Verwendung von relationalen Datenbanken hinweisen kann. Im Fall des Subselects werden Sie keine Näherungswerte sondern ein in sich konsistentes Ergebnis erhalten.

Subselects können in PostgreSQL nahezu überall eingesetzt werden. Häufig ist es auch notwendig, ein Subselect direkt in die SELECT-Clause zu setzen. Wollen wir beispielsweise den Durchschnittswert vergleichsweise neben den Preis stellen, können wir wie folgt vorgehen:

test=# SELECT *, (SELECT avg(price) FROM t_produkt) 
	FROM t_produkt;
 id |     name     | price |      ?column?
----+--------------+-------+---------------------
  1 | Semmel       |     1 | 44.3220000000000000
  2 | Käsestangerl |     2 | 44.3220000000000000
  3 | Kasten       |   216 | 44.3220000000000000
  4 | Rose         |  2.16 | 44.3220000000000000
  5 | Apfel        |  0.45 | 44.3220000000000000
(5 rows)

Wichtig ist, dass das Subselect nur exakt einen Wert zurückliefert - ist das nicht der Fall, wirft PostgreSQL einen Fehler aus.

Subselects können beliebig geschachtelt werden. Es ist problemlos möglich, ein Subselect in ein anderes Subselect zu integrieren:

test=# SELECT id, name, price - col AS diff 
	FROM (SELECT *, (SELECT avg(price) FROM t_produkt) AS col 
				FROM t_produkt) AS x;
 id |     name     |         diff
----+--------------+----------------------
  1 | Semmel       | -43.3220000000000000
  2 | Käsestangerl | -42.3220000000000000
  3 | Kasten       | 171.6780000000000000
  4 | Rose         | -42.1620000000000000
  5 | Apfel        | -43.8720000000000000
(5 rows)

In diesem Beispiel sehen Sie, wie man die Differenz zwischen dem Preis und dem Mittelwert berechnen kann.

Subselects können im Prinzip beliebiges SQL enthalten:

test=# SELECT gruppe, count(*) 
	FROM t_produkt_gruppe 
	GROUP BY gruppe;
    gruppe    | count
--------------+-------
 Lebensmittel |     3
 Möbel        |     1
 Botanik      |     2
(3 rows)

Selbst ein SQL-Statement, des Aggregates und dergleichen enthält, kann problemlos in ein Subselect integriert werden:

test=# SELECT z.* 
	FROM (SELECT gruppe, count(*) 
		FROM t_produkt_gruppe 
		GROUP BY gruppe) AS z 
	ORDER BY count DESC;
    gruppe    | count
--------------+-------
 Lebensmittel |     3
 Botanik      |     2
 Möbel        |     1
(3 rows)


Cybertec Schönig & Schönig GmbH
PostgreSQL support, training, consulting
www.postgresql-support.de