Verständnisproblem bei verschachteltem EXISTS



  • hallo,

    ich sitze gerade vor einer kleinen teilaufgabe die ich irgendwie nicht verstehe...
    also mir ist die lösung nicht klar und ich versuche das auch seit ca 1,5 std zu verstehen und irgendwie schritt für schritt durchzugehen doch es will nicht in meinen kopf...

    es geht um die nutzung von EXISTS in einem sql befehl...

    eigentlich verstehen ich wie EXISTS funktionieren solange es nur ein EXISTS ist,
    doch sobald da mehrere verschachtelt sind ist das einfach zu viel für meinen kopf und ich finde keine klare linie wie der befehl abgearbeitet wird oder wie man darauf kommen soll...

    hier mal die tabellen die vorliegen:
    http://s1.directupload.net/images/120704/kdbobrqk.png

    gefragt ist nun folgendes:
    welcher student besucht nicht alle vorlesungen, die 2 sws haben?

    die antwort soll so aussehen:

    MATR       PID
    ---------  --------
    10000      5
    12345      2
    

    und die lösung sieht wie folgt aus:

    select MATNR, PID from Student
    where exists ( select * from Veranstaltung
                   where SWS = 2
                   and not exists ( select * from VListe
                                    where VERAN = Veranstaltung.CODE
                                    and MATNR = Student.MATNR ))
    

    ich komme um ehrlich zu sein mit diesem befehl total nicht klar...
    das ganze könnte ich locker mit einem IN lösen doch man soll das mit EXISTS lösen und ich will das wirklich verstehen doch ich kann irgendwie nicht...

    in google finde ich auch nur beispiele und erklärungen mit einem EXISTS deswegen hoffe ich das eventuell hier jemand ist der mir das irgendwie gut erklären kann so das ich das verstehe wie man auf sowas kommen soll und wie das da jetzt eigentlich arbeitet...

    danke...!



  • Also...
    WHERE EXISTS(...) bedeutet im Prinzip WHERE COUNT_RECORDS(...) > 0
    Dabei ist COUNT_RECORDS() eine gedachte Funktion, die einfach guckt wie viele Zeilen ein SELECT ausspuckt.

    Damit das ganze nun sinnvoll anwendbar ist, muss man es natürlich mit den Daten der Zeile kombinieren können, die gerade mit WHERE gefiltert wird (die "aktuelle" Zeile des umgebenden SELECTs).
    Das passiert nun einfach dadurch, dass alle Bezeichner die nicht "lokal" aufgelöst werden können im "umgebenden" SELECT gesucht und ausgewertet werden.

    Angenommen du willst Veranstaltungen auflisten die ein bestimmter Student, dessen ID (Matrikelnummer) du bereits hast, besucht.
    Dann kannst du schreiben

    SELECT * FROM Veranstaltung
    WHERE EXISTS(
        SELECT * FROM VListe
        WHERE MATNR = 1234
          AND VERAN = Veranstaltung.Code
        )
    

    Veranstaltung.Code ist hier der Bezeichner der nicht "lokal" aufgelöst werden kann, er wird also im äusseren SELECT gesucht. Dort gibt es ihn: der "Code" der Veranstaltung der Zeile die gerade gefiltert wird.

    Bzw. wenn du wissen willst welche Veranstaltungen dieser Student besucht, die genau 2 Semesterwochenstunden haben, dann kannst du schreiben:

    SELECT * FROM Veranstaltung
    WHERE SWS = 2
      AND EXISTS(
        SELECT * FROM VListe
        WHERE VERAN = Veranstaltung.CODE
          AND MATNR = 1234
    )
    

    Klar soweit?

    Das dabei entstandene SELECT ist aber ein ganz normales SELECT, und kann natürlich seinerseits wieder in EXISTS verwendet werden.

    Jetzt wollen wir wissen welche Veranstaltungen, die genau 2 SWS haben, der Student NICHT besucht. Das ist einfach, wir stellen ein NOT vor das EXISTS:

    SELECT * FROM Veranstaltung
    WHERE SWS = 2
      AND >>NOT<< EXISTS(
        SELECT * FROM VListe
        WHERE VERAN = Veranstaltung.CODE
          AND MATNR = 1234
    )
    

    Und wenn wir jetzt wissen wollen, für welche Studenten es Veranstaltungen gibt, die genau 2 SWS haben, aber nicht vom Student besucht werden, dann müssen wir einfach für alle Studenten gucken ob die Abfrage die wir gerade gebastelt haben keine Zeilen ergibt.
    D.h.

    SELECT MATNR, PID FROM Student
    WHERE EXISTS(
        "Veranstaltungen die dieser Student nicht besucht, und die genau 2 SWS haben"
    )
    

    Jetzt musst du nur noch einsetzen. Und statt der fixen MATNR in der Abfrage den "äusseren Bezeichner" hinschreiben, der für die MATNR der Zeile des äussersten SELECTs steht. Und das ist dann:

    SELECT MATNR, PID FROM Student
    WHERE EXISTS(
        SELECT * FROM Veranstaltung
        WHERE SWS = 2
          AND NOT EXISTS(
            SELECT * FROM VListe
            WHERE VERAN = Veranstaltung.CODE
              AND MATNR = >>Student.MATNR<<
        )
    )
    

    Und genau das ist die Lösung.

    Achja, falls das der Knackpunkt für dich sein sollte: beim Bezeichner (Namen) auflösen geht es von innen nach aussen, Schritt für Schritt.
    Also "VERAN" z.B. wird schon im innersten SELECT gefunden, also nimmt die Abfrage es von dort.
    "Veranstaltung.CODE" gibt es im innersten SELECT nicht, dafür aber im "mittleren" (eins weiter aussen), also wird es von dort genommen.
    Und "Student.MATNR" gibt es weder im innersten noch im mittleren SELECT, also wird noch eins weiter aussen gesucht - im äusseren SELECT.
    Gäbe es dort auch keinen solchen Bezeichner, würde die SQL Abfrage entsprechend nicht compilieren.


Anmelden zum Antworten