Subselect / Unterabfragen)

Zur Durchführung einer Abfrage werden Informationen benötigt, die erst durch eine eigene Abfrage geholt werden müssen.

Sie können stehen

  • als Vertreter für einen Wert
  • als Vertreter für eine Liste
  • als Vertreter für eine Tabelle
  • als Vertreter für ein Feld

In [ ]:
%load_ext sql

In [3]:
%sql mysql://steinam:steinam@localhost/versicherung_complete


Out[3]:
'Connected: steinam@versicherung_complete'


In [1]:
% load_ext sql

Vertreter für Wert

Nenne alle Mitarbeiter der Abteilung „Schadensabwicklung“.


In [ ]:
%sql

### Lösung:
    
-- Lösung Teil 1: Hole die ID dieser Abteilung anhand des Namens. 
-- Lösung Teil 2: Hole die Mitarbeiter der Abteilung unter Benutzung der gefundenen ID. 
    
select Personalnummer, Name, Vorname 
from Mitarbeiter 
where Abteilung_ID = 
( select ID from Abteilung 
where Kuerzel = ScAb );

Lösung


In [ ]:
%%sql

select Personalnummer, Name, Vorname 
from Mitarbeiter 
where Abteilung_ID = 
( select ID from Abteilung 
where Kuerzel = 'ScAb' );

Vertreter für Spaltenfunktionen

Die Ergebnisse von Aggregatfunktionen werden häufig in der WHERE-Klausel benötigt

Beispiel:

Hole die Schadensfälle mit unterdurchschnittlicher Schadenshöhe.

Lösung

  • Teil 1: Berechne die durchschnittliche Schadenshöhe aller Schadensfälle.
  • Teil 2: Übernimm das Ergebnis als Vergleichswert in die eigentliche Abfrage.

In [13]:
%%sql
SELECT ID, Datum, Ort, Schadenshoehe 
from Schadensfall 
where Schadenshoehe < ( 
        select AVG(Schadenshoehe) from Schadensfall 
);


5 rows affected.
Out[13]:
ID Datum Ort Schadenshoehe
1 2007-02-03 Recklinghausen, Bergknappenstr. 144 1234.50
2 2007-07-11 Haltern, Hauptstr. 46 2066.00
4 2008-05-27 Recklinghausen, Südgrabenstr. 23 1438.75
5 2008-10-05 Dorsten, Oberhausener Str. 18 1983.00
7 2009-08-21 Recklinghausen, Bergknappenstr. 144 865.00

Aufgabe

Bestimme alle Schadensfälle, die von der durchschnittlichen Schadenshöhe eines Jahres maximal 300 € abweichen.

Lösung

  • Teil 1: Bestimme den Durchschnitt aller Schadensfälle innerhalb eines Jahres.
  • Teil 2: Hole alle Schadensfälle, deren Schadenshöhe im betreffenden Jahr innerhalb des Bereichs „Durchschnitt plus/minus 300“ liegen.

In [14]:
%%sql

select sf.ID, sf.Datum, sf.Schadenshoehe, EXTRACT(YEAR from 
sf.Datum) AS Jahr 
from Schadensfall sf 
where ABS(Schadenshoehe - ( 
        select AVG(sf2.Schadenshoehe) 
           from Schadensfall sf2 
           where YEAR(sf2.Datum) = YEAR(sf.Datum) 
           ) 
          ) <= 300;


4 rows affected.
Out[14]:
ID Datum Schadenshoehe Jahr
2 2007-07-11 2066.00 2007
4 2008-05-27 1438.75 2008
5 2008-10-05 1983.00 2008
8 2009-08-01 2471.50 2009

Bemerkung

Dies ist ein Paradebeispiel dafür, wie Unterabfragen nicht benutzt werden sollen. Für jeden einzelnen Datensatz muss in der WHERE-Bedingung eine neue Unterabfrage gestartet werden − mit eigener WHERE-Klausel und Durchschnittsberechnung. Viel besser wäre eine der JOIN-Varianten.

Weitere Lösungsmöglichkeiten (Lutz (13/14)

select beschreibung, schadenshoehe 
from schadensfall where 
schadenshoehe <= ( 
select avg(schadenshoehe) 
from schadensfall) + 300 
and schadenshoehe >= (select avg(schadenshoehe) 
from schadensfall) - 300 


select beschreibung, schadenshoehe 
from schadensfall where 
schadenshoehe between ( 
select avg(schadenshoehe) 
from schadensfall) - 300 
and (select avg(schadenshoehe) 
from schadensfall) + 300 


select @average:=avg(schadenshoehe) from schadensfall; 
select  id  from  schadensfall  where  abs(schadenshoehe  - 
@average) <= 300;

Ergebnis als Liste mehrerer Werte

Das Ergebnis einer Abfrage kann als Filter für die eigentliche Abfrage benutzt werden.

Aufgabe

Bestimme alle Fahrzeuge eines bestimmten Herstellers.

Lösung

  • Teil 1: Hole die ID des gewünschten Herstellers.
  • Teil 2: Hole alle IDs der Tabelle Fahrzeugtyp zu dieser Hersteller-ID.
  • Teil 3: Hole alle Fahrzeuge, die zu dieser Liste von Fahrzeugtypen-IDs passen.

In [16]:
%%sql

select ID, Kennzeichen, Fahrzeugtyp_ID as TypID 
from Fahrzeug 
where Fahrzeugtyp_ID in( 
        select ID 
        from Fahrzeugtyp 
        where Hersteller_ID = ( 
                select ID 
                from Fahrzeughersteller 
                where Name = 'Volkswagen' ) );


2 rows affected.
Out[16]:
ID Kennzeichen TypID
23 BOR-RS 890 2
22 BOR-PQ 567 3

Aufgabe

Gib alle Informationen zu den Schadensfällen des Jahres 2008, die von der durchschnittlichen Schadenshöhe 2008 maximal 300 € abweichen.

Lösung

  • Teil 1: Bestimme den Durchschnitt aller Schadensfälle innerhalb von 2008.
  • Teil 2: Hole alle IDs von Schadensfällen, deren Schadenshöhe innerhalb des Bereichs „Durchschnitt plus/minus 300“ liegen.
  • Teil 3: Hole alle anderen Informationen zu diesen IDs.

In [17]:
%%sql

select * 
from Schadensfall 
where ID in ( SELECT ID 
from Schadensfall 
where ( ABS(Schadenshoehe - ( 
           select AVG(sf2.Schadenshoehe) 
           from Schadensfall sf2 
           where YEAR(sf2.Datum) = 2008 
           ) 
      ) <= 300 ) 
and ( YEAR(Datum) = 2008 ) 
);


2 rows affected.
Out[17]:
ID Datum Ort Beschreibung Schadenshoehe Verletzte Mitarbeiter_ID
4 2008-05-27 Recklinghausen, Südgrabenstr. 23 Fremdes parkendes Auto gestreift 1438.75 N 16
5 2008-10-05 Dorsten, Oberhausener Str. 18 beim Ausparken hat ein fremder Wagen die Vorfahrt missachtet 1983.00 N 14

Vertreter für eine Tabelle

Das Ergebnis einer Abfrage kann in der Hauptabfrage überall dort eingesetzt werden, wo eine Tabelle vorgesehen ist. Die Struktur dieser Situation sieht so aus:

SELECT <spaltenliste> 
FROM <haupttabelle>, 
  (SELECT <spaltenliste> 
   FROM <zusatztabellen> 
<weitere Bestandteile der Unterabfrage> 
) <name> 
<weitere Bestandteile der Hauptabfrage>
  • Die Unterabfrage kann grundsätzlich alle SELECT-Bestandteile enthalten.
  • ORDER BY kann nicht sinnvoll genutzt werden, weil das Ergebnis der Unterabfrage mit der Haupttabelle oder einer
    anderen Tabelle verknüpft wird wodurch eine Sortierung sowieso verlorenginge.
  • Es muss ein Name als Tabellen-Alias angegeben werden, der als Ergebnistabelle in der Hauptabfrage verwendet wird.

Aufgabe

Bestimme alle Schadensfälle, die von der durchschnittlichen Schadenshöhe eines Jahres maximal 300 € abweichen.

Lösung

  • Teil 1: Stelle alle Jahre zusammen und bestimme den Durchschnitt aller Schadensfälle innerhalb eines Jahres.
  • Teil 2: Hole alle Schadensfälle, deren Schadenshöhe im jeweiligen Jahr innerhalb des Bereichs „Durchschnitt plus/minus 300“ liegen.

In [ ]:
%sql

SELECT  sf.ID,  sf.Datum,  sf.Schadenshoehe,  temp.Jahr, 
temp.Durchschnitt 
FROM Schadensfall sf, 
   ( SELECT AVG(sf2.Schadenshoehe) AS Durchschnitt, 
         EXTRACT(YEAR FROM sf2.Datum) as Jahr 
         FROM Schadensfall sf2 
           group by EXTRACT(YEAR FROM sf2.Datum) 
    ) temp 
WHERE temp.Jahr = EXTRACT(YEAR FROM sf.Datum) 
and ABS(Schadenshoehe - temp.Durchschnitt) <= 300;
  • Durch eine Gruppierung werden alle Jahreszahlen und die durchschnittlichen Schadenshöhen zusammengestellt (Teil 1 der Lösung).
  • Für Teil 2 der Lösung muss für jeden Schadensfall nur noch Jahr und Schadenshöhe mit dem betreffenden Eintrag in der Ergebnistabelle temp verglichen werden.

Das ist der wesentliche Unterschied und entscheidende Vorteil zu anderen Lösungen: Die Durchschnittswerte werden einmalig zusammengestellt und nur noch abgerufen; sie müs- sen nicht bei jedem Datensatz neu (und ständig wiederholt) berechnet werden.

Aufgabe

Bestimme alle Fahrzeuge eines bestimmten Herstellers mit Angabe des Typs.

  • Teil 1: Hole die ID des gewünschten Herstellers.
  • Teil 2: Hole alle IDs und Bezeichnungen der Tabelle Fahrzeugtyp, die zu dieser Hersteller-ID gehören.
  • Teil 3: Hole alle Fahrzeuge, die zu dieser Liste von Fahrzeugtyp-IDs gehören.

In [19]:
%%sql

SELECT Fahrzeug.ID, Kennzeichen, Typen.ID As TYP, Typen.Bezeichnung 
FROM Fahrzeug, 
     (SELECT ID, Bezeichnung 
        FROM Fahrzeugtyp 
        WHERE Hersteller_ID = 
           (SELECT ID 
             FROM Fahrzeughersteller 
             WHERE Name = 'Volkswagen' ) 
      ) Typen 
WHERE Fahrzeugtyp_ID = Typen.ID;


2 rows affected.
Out[19]:
ID Kennzeichen TYP Bezeichnung
23 BOR-RS 890 2 Golf
22 BOR-PQ 567 3 Passat

Übungen

  1. Welche der folgenden Feststellungen sind richtig, welche sind falsch?

    1. Das Ergebnis einer Unterabfrage kann verwendet werden, wenn es ein einzelner Wert oder eine Liste in Form einer Tabelle ist. Andere Ergebnisse sind nicht möglich.
    2. Ein einzelner Wert als Ergebnis kann durch eine direkte Abfrage oder durch eine Spaltenfunktion erhalten werden.
    3. Unterabfragen sollten nicht verwendet werden, wenn die WHERE-Bedingung für jede Zeile der Hauptabfrage einen anderen Wert erhält und deshalb die Unterabfrage neu ausgeführt werden muss.
    4. Mehrere Unterabfragen können verschachtelt werden.
    5. Für die Arbeitsgeschwindigkeit ist es gleichgültig, ob mehrere Unterabfragen oder JOINs verwendet werden.
    6. Eine Unterabfrage mit einer Tabelle als Ergebnis kann GROUP BY nicht sinnvoll nutzen.
    7. Eine Unterabfrage mit einer Tabelle als Ergebnis kann ORDER BY nicht sinnvoll nutzen.
    8. Bei einer Unterabfrage mit einer Tabelle als Ergebnis ist ein Alias-Name für die Tabelle sinnvoll, aber nicht notwendig.
    9. Bei einer Unterabfrage mit einer Tabelle als Ergebnis sind Alias-Namen für die Spalten sinnvoll, aber nicht notwendig.
  2. Welche Verträge (mit einigen Angaben) hat der Mitarbeiter „Braun, Christian“ abgeschlossen? Ignorieren Sie die Möglichkeit, dass es mehrere Mitarbeiter dieses Namens geben könnte.

  3. Zeigen Sie alle Verträge, die zum Kunden 'Heckel Obsthandel GmbH' gehören. Ignorieren Sie die Möglichkeit, dass der Kunde mehrfach gespeichert sein könnte.
  4. Ändern Sie die Lösung von Übung 3, sodass auch mehrere Kunden mit diesem Namen als Ergebnis denkbar sind.
  5. Zeigen Sie alle Fahrzeuge, die im Jahr 2008 an einem Schadensfall beteiligt waren.
  6. Zeigen Sie alle Fahrzeugtypen (mit ID, Bezeichnung und Name des Herstellers), die im Jahr 2008 an einem Schadensfall beteiligt waren.
  7. Bestimmen Sie alle Fahrzeuge eines bestimmten Herstellers mit Angabe des Typs.
  8. Zeigen Sie zu jedem Mitarbeiter der Abteilung „Vertrieb“ den ersten Vertrag (mit einigen Angaben) an, den er abgeschlossen hat. Der Mitarbeiter soll mit ID und Name/Vorname angezeigt werden.
  9. Von der Deutschen Post AG wird eine Tabelle PLZ_Aenderung mit folgenden Inhalten geliefert:
csv
ID PLZalt Ortalt          PLZneu Ortneu 
1 45658   Recklinghausen  45659  Recklinghausen 
2 45721   Hamm-Bossendorf 45721  Haltern OT Hamm 
3 45772   Marl            45770  Marl 
4 45701   Herten          45699  Herten
Ändern Sie die Tabelle Versicherungsnehmer so, dass bei allen Adressen, bei denen PLZ/Ort mit PLZalt/Ortalt  
übereinstimmen, diese Angaben durch PLZneu/Ortneu geändert werden. 

Hinweise: Beschränken Sie sich auf die Änderung mit der ID=3. (Die vollständige Lösung ist erst mit   
SQL-Programmierung möglich.) Bei dieser Änderungsdatei handelt es sich nur um fiktive Daten, keine echten Änderungen. 

Sommer 2016


In [ ]:
%sql mysql://steinam:steinam@localhost/versicherung_complete

New heading