Matthias Apsel: Teilmengen

Om nah hoo pez nyeetz, alle!

Ich suche mit einer etwas komplexeren Abfrage bestimmte Schüler aus einer Klasse heraus, nämlich die, die noch unerledigte Aufgaben haben.

Ich suche jetzt alle Schüler dieser Klasse, die in dieser Abfrage nicht dabei sind, d.h. die bereits alle Aufgaben erledigt haben.

Wie stellt man sowas am geschicktesten an?

(MySQL PDO)

Im adminer ist dieses SQL zielführend.

CREATE TEMPORARY TABLE Mahnung ENGINE=INNODB  
SELECT  
   Schüler.ID AS ID,  
   Name,  
   Vorname,  
   `Text`  
FROM (  
   Kreuztabelle  
   INNER JOIN  
      Schüler  
      ON Kreuztabelle.SchülerID = Schüler.ID  
   )  
INNER JOIN  
   Aufgaben  
   ON Kreuztabelle.AufgabenID = Aufgaben.ID  
WHERE  
   Schüler.Klasse = 29 AND  
   Status = 'o' AND  
   Fälligkeit < '2014-03-09' AND  
   Erlass_k = 0 AND  
   Erlass_n = 0 ;  
  
SELECT * FROM Mahnung; -- alle Schüler mit unerledigten Aufgaben  
  
SELECT  
   Schüler.Name,  
   Schüler.Vorname  
FROM Schüler  
LEFT JOIN  
   Mahnung  
   ON Schüler.ID = Mahnung.ID  
WHERE  
   ISNULL(Mahnung.ID) AND  
   Schüler.Klasse = 29; -- alle die, die in Mahnung nicht vorkommen  
  
DROP TABLE Mahnung;

Leider krieg ich das mit PHP nicht umgesetzt.

Matthias

--
Der Unterschied zwischen Java und JavaScript ist größer als der zwischen Auto und Automat.

  1. Vorwort: Ich bin im Hotel und habe also meinen Nachtrunk schon genommen.

    Matthias

    Äh. Der mir aus diesem Forum bekannte Matthias Apsel?

    Leider krieg ich das mit PHP nicht umgesetzt.

    Hm. Wieso nicht?
    1)
    Hat Dein Benutzer keine Rechte um temporäre Tabellen anzulegen?

    Hast Du nicht beachtet, dass es vier (4) Abfragen sind?

    Erste:

    CREATE TEMPORARY TABLE Mahnung ENGINE=INNODB  
    SELECT  
       Schüler.ID AS ID,  
       Name,  
       Vorname,  
       `Text`  
    FROM (  
       Kreuztabelle  
       INNER JOIN  
          Schüler  
          ON Kreuztabelle.SchülerID = Schüler.ID  
       )  
    INNER JOIN  
       Aufgaben  
       ON Kreuztabelle.AufgabenID = Aufgaben.ID  
    WHERE  
       Schüler.Klasse = 29 AND  
       Status = 'o' AND  
       Fälligkeit < '2014-03-09' AND  
       Erlass_k = 0 AND  
       Erlass_n = 0 ;
    

    Zweite:

    SELECT * FROM Mahnung; -- alle Schüler mit unerledigten Aufgaben  
    
    

    Dritte:

    SELECT  
       Schüler.Name,  
       Schüler.Vorname  
    FROM Schüler  
    LEFT JOIN  
       Mahnung  
       ON Schüler.ID = Mahnung.ID  
    WHERE  
       ISNULL(Mahnung.ID) AND  
       Schüler.Klasse = 29; -- alle die, die in Mahnung nicht vorkommen
    

    Vierte:
    DROP TABLE Mahnung;

    Die musst/solltest Du auch einzeln abschicken.

    Jörg Reinholz

    1. Om nah hoo pez nyeetz, Jörg Reinholz!

      Matthias
      Äh. Der mir aus diesem Forum bekannte Matthias Apsel?

      Genau der.

      Hat Dein Benutzer keine Rechte um temporäre Tabellen anzulegen?

      Hast Du nicht beachtet, dass es vier (4) Abfragen sind?
      Die musst/solltest Du auch einzeln abschicken.

      Danke fuer die Ideen, ich weiss jetzt, wo ich schauen kann.

      Matthias

      --
      Der Unterschied zwischen Java und JavaScript ist größer als der zwischen Sandal und Sandalette.

  2. Hi,

    Ich suche jetzt alle Schüler dieser Klasse, die in dieser Abfrage nicht dabei sind, d.h. die bereits alle Aufgaben erledigt haben.

    im Prinzip so:

    SELECT * FROM schüler s WHERE s.klassenid = <gesuchteKlasse> AND s.id NOT IN
    (SELECT id FROM schüler WHERE <unerledigte aufgaben vorhanden>)

    cu,
    Andreas

    --
    Warum nennt sich Andreas hier MudGuard?
    O o ostern ...
    Fachfragen per Mail sind frech, werden ignoriert. Das Forum existiert.
    1. Om nah hoo pez nyeetz, MudGuard!

      SELECT * FROM schüler s WHERE s.klassenid = <gesuchteKlasse> AND s.id NOT IN
      (SELECT id FROM schüler WHERE <unerledigte aufgaben vorhanden>)

      Ist zumindest schon mal kuerzer als meine Loesung mit dem join. Danke.

      Matthias

      --
      Der Unterschied zwischen Java und JavaScript ist größer als der zwischen scheu und Scheune.

      1. UNION, EXCEPT und INTERSECT sind typischerweise die Stichworte fuer "Mengen" in SQL Datenbanken :-)   NOT IN bzw. IN tutet es aber in 90% der Faelle auch schon.

        Frank

  3. Om nah hoo pez nyeetz, alle!

    Von dedlfix habe ich eine Lösung erhalten, die ohne eine temporäre Tabelle auskommt.

    SELECT  
    	s.Name,  
    	s.Vorname,  
    	a.Text,  
    	a.Kategorie  
    FROM  
    	Schüler s  
    LEFT JOIN  
    	Kreuztabelle k  
    	ON k.SchülerID = s.ID AND  
    	k.Status = 'o' AND  
    	k.Erlass_k = 0 AND  
    	k.Erlass_n = 0 AND  
    	EXISTS (  
                    SELECT  
                            *  
                    FROM  
                            Aufgaben a  
                    WHERE  
                            a.ID = k.AufgabenID AND  
                            a.Fälligkeit < '2014-03-10'  
                   )  
    LEFT JOIN  
            Aufgaben a ON a.ID = k.AufgabenID  
    WHERE  
            s.Klasse = 29
    

    Diese Abfrage liefert zunächst einmal alle Ergebnissätze Schüler - offene Aufgaben. Für die Schüler ohne offene Aufgaben schlägt das SELECT innerhalb von EXISTS fehl, weshalb dann Ergebnissätze

    Name, Vorname, NULL, NULL  
    
    

    entstehen, die sich in PHP von einander trennen lassen:

    while ($mahnung = $stmt -> fetch(PDO::FETCH_ASSOC)) {  
        if($mahnung['Text'] != '') {  
            // Schüler mit offenen Aufgaben  
        }  
        else {  
            // Schüler ohne offene Aufgaben  
        }  
    }
    

    Matthias

    --
    Der Unterschied zwischen Java und JavaScript ist größer als der zwischen Mona und Monaco.

    1. Hi,

      Diese Abfrage liefert zunächst einmal alle Ergebnissätze Schüler - offene Aufgaben. Für die Schüler ohne offene Aufgaben schlägt das SELECT innerhalb von EXISTS fehl, weshalb dann Ergebnissätze

      Name, Vorname, NULL, NULL

      
      > entstehen, die sich in PHP von einander trennen lassen:  
        
      Und warum nicht gleich beim SQL rausschmeißen?  
      INNER statt LEFT JOIN. Oder " AND a.text IS NOT NULL" im WHERE- oder ON-Teil?  
        
        
      "Kreuztabelle" find ich als Tabellennamen seltsam.  
      Da würde ich doch eher was nehmen, was aussagt, welche Tabellen hier verbunden werden, also "schueler\_aufgaben" oder so.  
      Bei einer größeren DB hast Du sonst schnell Kreuztabelle17, Kreuztabelle42, ...  
        
      cu,  
      Andreas
      
      -- 
      [Warum nennt sich Andreas hier MudGuard?](http://MudGuard.de/)  
      [O o ostern ...](http://ostereier.andreas-waechter.de/)  
        
      Fachfragen per Mail sind frech, werden ignoriert. Das Forum existiert.  
      
      
      1. Om nah hoo pez nyeetz, MudGuard!

        Und warum nicht gleich beim SQL rausschmeißen?
        INNER statt LEFT JOIN. Oder " AND a.text IS NOT NULL" im WHERE- oder ON-Teil?

        Weil ich beide Gruppen brauche (siehe OP) ;-)

        "Kreuztabelle" find ich als Tabellennamen seltsam.
        Da würde ich doch eher was nehmen, was aussagt, welche Tabellen hier verbunden werden, also "schueler_aufgaben" oder so.
        Bei einer größeren DB hast Du sonst schnell Kreuztabelle17, Kreuztabelle42, ...

        Das stimmt. Wollen wir mal hoffen, dass ich es nie mit einer größeren DB zu tun haben werde. Ich tu mich bei dieser schon recht schwer.

        Matthias

        --
        Der Unterschied zwischen Java und JavaScript ist größer als der zwischen Nacht und Nachtisch.