9 Korrelierte Subqueries

Werbung
150.422
9
Datenbanken – SQL Handout 5
Korrelierte Subqueries
In gewöhnlichen Subqueries wird die innere Abfrage einmal und unabhängig von der äußeren Abfrage
durchgeführt. In korrelierten Subqueries wird die innere Abfrage mehrmals durchgeführt, jedesmal für
einen anderen Parameter, der von der äußeren Abfrage kommt.
B Beispiele. Finde für jeden Teil den Lieferanten mit dem besten Angebot (d.h. kleinster Preis).
Mit einer gewöhnlichen Subquery lässt sich der günstigste Lieferant für einen gegebenen Teil finden:
1
SELECT S_No FROM Order_Options
WHERE P_No = ’ ... ’
AND Price_per_Unit =
( SELECT MIN ( Price_per_Unit ) FROM Order_Options
WHERE P_No = ’ ... ’ )
Um ein Ergebnis für alle Teile mit einer einzigen Abfrage zu erhalten, verwenden wir eine korrelierte
Subquery:
SELECT P_No , S_No , Price_per_Unit FROM Order_Options a
WHERE Price_per_Unit =
( SELECT MIN ( Price_per_Unit ) FROM Order_Options b
WHERE b . P_No = a . P_No )
Intern führt diese Abfrage eine Schleife über alle Teile aus: Die äußere Abfrage übergibt der inneren der
Reihe nach jede einzelne P No (entspricht dem a.P No in der Abfrage). Für jede P No wird dann die
innere Abfrage ausgeführt, um den entsprechenden kleinsten Preis zu finden. Dieser wird wieder an die
äußere Abfrage übergeben, die den Lieferanten zum kleinsten Preis sucht.
Regel: Möchte man für mehrere Werte eines Attributs A all jene Attribute B
finden, für die A ein Maximum oder Minimum annimmt, so muss eine korrelierte
Subquery verwendet werden.
Achtung: Zusätzliche Bedingungen müssen typischerweise in die innere und die äußere Abfrage platziert
werden! Möchte man im ersten Beispiel etwa nur inländische Lieferanten berücksichtigen, würde die
Abfrage wie folgt aussehen. (Um an die Länderinformation zu kommen, ist ein zusätzlicher Join nötig!)
1
6
SELECT P_No , a . S_No , Price_per_Unit FROM Order_Options a JOIN Supplier s
ON s . S_No = a . S_No
WHERE s . S_Countrycode = ’A ’
AND Price_per_Unit =
( SELECT MIN ( Price_per_Unit ) FROM Order_Options b JOIN Supplier t
ON t . S_No = b . S_No
WHERE b . P_No = a . P_No
AND t . S_Countrycode = ’A ’ )
Ohne die Länderbedingung in der äußeren Abfrage würden natürlich auch ausländische Firmen angezeigt werden, die ein Teil zum gleichen Preis anbieten wie der günstigste inländische Lieferant. Fehlt die
Länderbedingung in der inneren Abfrage, so ermittelt die innere Abfrage den günstigsten Preis aller Anbieter. Wird der Teil nur von ausländischen Lieferanten am günstigsten angeboten, so findet die äußere
Abfrage keinen inländischen Lieferanten zum entsprechenden Teil, und dieses Teil fehlt im Ergebnis.
Zeige alle Angebote, bei denen der Preis eines Teils unter dem durchschnittlichen Preis dieses Teils
liegt.
2
SELECT P_No , S_No , Price_per_Unit FROM Order_Options a
WHERE Price_per_Unit <
( SELECT AVG ( Price_per_Unit ) FROM Order_Options b
WHERE b . P_No = a . P_No )
Ähnlich zu den obigen Abfragen berechnet die innere Abfrage der Reihe nach den durchschnittlichen
Preis jedes einzelnen Teils und gibt diesen an die äußere Abfrage zurück.
SS 2017
Man beachte, dass eine Änderung der Bedingung in der inneren Abfrage natürlich ein anderes Ergebnis
liefert. Ersetzt man in den obigen Abfragen etwa die Bedingung b.P No = a.P No durch b.S No =
a.S No, so berechnet die innere Query den günstigsten bzw. durchschnittlichen Preis pro Lieferanten.
B Beispiele. Zeige für jeden Lieferanten das günstigste Teil, das von ihm angeboten wird.
1
SELECT P_No , S_No , Price_per_Unit FROM Order_Options a
WHERE Price_per_Unit = ( SELECT MIN ( Price_per_Unit ) FROM Order_Options b
WHERE b . S_No = a . S_No )
Zeige alle Angebote, bei denen ein Lieferant ein Teil unter dem Durchschnittspreis eines Teiles bei
diesem Lieferanten anbietet.
2
SELECT P_No , S_No , Price_per_Unit FROM Order_Options a
WHERE Price_per_Unit < ( SELECT AVG ( Price_per_Unit ) FROM Order_Options b
WHERE b . S_No = a . S_No )
• ALL und ANY • können in (gewöhnlichen wie korrelierten) Subqueries verwendet werden, um eine
Bedingung für alle oder einige von einer Subquery zurückgegebenen Werte zu überprüfen. (Ähnlich wie
bei IN darf eine solche Subquery also auch mehrere Zeilen zurückgeben.)
B Examples. Zeige alle Angebote aus Order Options, bei denen der Preis unter dem Preis aller
Angebote für Teil DL481C576 liegt.
2
SELECT P_No , S_No , Price_Per_Unit from Order_Options
WHERE Price_Per_Unit < ALL
( SELECT Price_Per_Unit FROM Order_Options
WHERE P_No = ’ DL481C576 ’)
Zeige alle Angebote aus Order Options, bei denen der Preis unter dem Preis (mindestens) eines
Angebotes für Teil DL481C576 liegt.
1
SELECT P_No , S_No , Price_Per_Unit from Order_Options
WHERE Price_Per_Unit < ANY
( SELECT Price_Per_Unit FROM Order_Options
WHERE P_No = ’ DL481C576 ’)
Finde für jeden Teil den Lieferanten mit dem besten Angebot (d.h. kleinster Preis).
Alternativ zur oben gezeigten Lösung mit MIN, kann dieses Problem auch mit ALL gelöst werden.
1
SELECT P_No , S_No , Price_Per_Unit FROM Order_Options a
WHERE Price_Per_Unit <= ALL
( SELECT Price_Per_Unit FROM Order_Options b
WHERE a . P_No = b . P_No )
• EXISTS • wird dazu verwendet, um zu überprüfen, ob eine Subquery eine nichtleere Ergebnismenge
(d.h. mindestens eine Zeile) zurückgibt. Meist ist der Einsatz von EXISTS nur in Verbindung mit einer
korrelierten Subquery sinnvoll.
B Beispiele. Zeige alle Angebote aus Order Options für Teile, für die es mindestens ein Angebot
unter 1 Euro gibt.
1
SELECT P_No , S_No , Price_Per_Unit FROM Order_Options a
WHERE EXISTS
( SELECT * FROM Order_Options b
WHERE a . P_No = b . P_No and Price_Per_Unit < 1)
Analog kann mit NOT EXISTS überprüft werden, ob eine Subquery eine leere Ergebnismenge zurückgibt.
Abfragen mit ANY bzw. ALL können für gewöhnlich so umgeschrieben werden, dass stattdessen MIN
bzw. MAX verwendet wird. Ähnlich kann statt EXISTS eine Abfrage mit IN verwendet werden.
Herunterladen