Andreas: Problem mit komplizierter SQL-Abfrage:

Hallo!
Ich habe eine recht komplizierte SQL-Abfrage, die einfach nicht funktionieren will. Das Problem, ich benutze gleichzeitig Joins und Aggregats-Funktionen, meine beiden Lieblingsfehlerquellen, aber zusammen blicke  ich da nicht mehr durch. Erstmal zur Abfrage:
Ich habe 2 Tabellen, Bestellungen(allgemeine Daten pro Bestellung) und Bestelldaten(alle einzelnen bestellten Produkte, hier wird die zugehörige Bestellungs-ID gespeichert).
In der Tabelle Bestellungen habe ich die Lieferanten ID gespeichert, und jetzt suche ich die Summe aller Preise (die in der Tabelle Bestelldaten gespeichert sind) die einer bestimmten Lieferanten ID zugeordnet sind. Etwas kompliziert(formuliert:), leichter ist das mit der Abfrage, die ich probiert habe:

SELECT sum(t1.Preis)
FROM Bestelldaten AS t1
LEFT JOIN Bestellungen AS t2 ON t1.Bestellungen_ID = t2.ID
GROUP BY t1.Preis
HAVING t2.Lieferanten_ID = $Lieferanten_ID

group by Preis hab ich núr so geschrieben, da ich in Erinnerung hatte, das man da was schreiben muß, da SUM einfach mit where nicht geht.

Hat jemand eine Idee wie man das anders machen könnte, oder wo ein möglicher Fehler liegt?

Vielen Dank!

Andreas

  1. FROM Bestelldaten AS t1

    Das AS auch für Tabellen geht wäre mir neu.

    Falls das nicht der Fehler ist, dann poste mal bitte die Fehlermeldung...

    Gruss,
    CS

    1. FROM Bestelldaten AS t1

      Doch, das geht! Das mache ich oft erfolgreich mit einfachen Joins, so muß man nicht immer den ganzen Tabellennamen schreiben. Die Fehlermeldung sagt nicht wirklich viel aus:

      Warning: Supplied argument is not a valid MySQL result resource in Line 159

      in der Zeile steht

      mysql_fetch_array($res);

      $res bezieht sich auf obige SQL-Abfrage.

      Ich habe noch nie JOINS und SUM in einer Anfrage verwendet, habe keine Ahnung ob das so geht, oder ob überhaupt. Nun ja!

      Grüße
        Andras

      1. Doch, das geht! Das mache ich oft erfolgreich mit einfachen Joins, so muß man nicht immer den ganzen Tabellennamen schreiben. Die Fehlermeldung sagt nicht wirklich viel aus:

        Aha... Wusste ich nicht mal... Danke! :o))

        Warning: Supplied argument is not a valid MySQL result resource in Line 159

        Von MySQL versteh ich nicht viel, aber es sieht so aus aus würde $res nicht im Result-Set sein.
        Was ist das denn? Eine Variable? Ist sie definiert?

        Gruss,
        CS

        1. Doch, das geht! Das mache ich oft erfolgreich mit einfachen Joins, so muß man nicht immer den ganzen Tabellennamen schreiben. Die Fehlermeldung sagt nicht wirklich viel aus:

          Aha... Wusste ich nicht mal... Danke! :o))

          Warning: Supplied argument is not a valid MySQL result resource in Line 159

          Von MySQL versteh ich nicht viel, aber es sieht so aus aus würde $res nicht im Result-Set sein.
          Was ist das denn? Eine Variable? Ist sie definiert?

          Gruss,
          CS

          Hier liegt eindeutig ein Mißverständnis vor. Die SQL-Abfrage schlug fehl. Die weitere Verwendung von $res (wahrscheinlich das fetchen) führt zu benannter Fehlermeldung. Die eigentliche SQL-Fehlermeldung bekommt man aber über die SQL-Shell heraus. ERGO:

          Bei Datenbank in der Shell oder einem entsprechenden Frontend anmelden, "USE databasename;" und dann "SELECT ...".

          Gruß,
          Andreas

          1. Hier liegt eindeutig ein Mißverständnis vor.

            Richtig.

            Bei Datenbank in der Shell oder einem entsprechenden Frontend anmelden, "USE databasename;" und dann "SELECT ...".

            Das ist interessant, wenn die DB aber nur über den Apache zu erreichen ist, nicht über den Port direkt, geht das denn dann mit der SHELL? Wie detailiert sind denn die Fehlermeldungen dann? Würde dann da auch stehen z.B. Spaltennamen gibt es nicht..."(war eines meiner Probleme:) und ähnliches?
            Dann würde ich mir das doch nochmal alles Lokal installieren und so testen und eine Menge Zeit sparen:)

            Grüße
              Andreas

  2. Hallo Andreas,

    Ich habe 2 Tabellen, Bestellungen(allgemeine Daten pro Bestellung) und Bestelldaten(alle einzelnen bestellten Produkte, hier wird die zugehörige Bestellungs-ID gespeichert).
    In der Tabelle Bestellungen habe ich die Lieferanten ID gespeichert, und jetzt suche ich die Summe aller Preise (die in der Tabelle Bestelldaten gespeichert sind) die einer bestimmten Lieferanten ID zugeordnet sind. Etwas kompliziert(formuliert:), leichter ist das mit der Abfrage, die ich probiert habe:

    SELECT sum(t1.Preis)
    FROM Bestelldaten AS t1
    LEFT JOIN Bestellungen AS t2 ON t1.Bestellungen_ID = t2.ID
    GROUP BY t1.Preis
    HAVING t2.Lieferanten_ID = $Lieferanten_ID

    ich komme zwar von anderen SQL-Dialekten, aber vielleicht geht es so:

    SELECT be.ID,sum(bd.Preis)
          FROM Bestelldaten AS bd,Bestellungen AS be
          WHERE bd.Bestellungen_ID = be.ID
          GROUP BY bd.Bestellungen_ID

    Dabei ist dieses AS in der FROM-Klausel sicher nicht ANSI-konform. Außerdem könnte Deine Nomenklatur (Tabellen- und Spaltennamen) aussagekräftiger sein; ich mußte dreimal nachlesen, bevor ich eine Ahnung bekam - bin immer noch nicht sicher - was Bestelldaten und was Bestellungen überhaupt bedeutet. Wenn Du eine 1:n-Beziehung abbildest, könntest Du zum Beispiel mit den Präfixen "M" für Master und "C" für Child arbeiten, dann weiß jeder Fachmann sofort, was gemeint ist.

    group by Preis hab ich núr so geschrieben, da ich in Erinnerung hatte, das man da was schreiben muß, da SUM einfach mit where nicht geht.

    Die GROUP-BY-Klausel verwendet man nicht einfach so, sondern sie sagt dem SELECT, nach welchem Kriterium die Spaltenqualifizierer angewendet werden sollen; in diesem Fall also, über welche Menge von DS die Summenbildung vorgenommen werden soll.

    Die Notwendigkeit für JOINS und die HAVING-Klausel erkenne ich nicht - sofern ich die Aufgabenstellung richtig interpretiert habe.

    HTH Robert

    1. Hallo Andreas,

      Ich habe 2 Tabellen, Bestellungen(allgemeine Daten pro Bestellung) und Bestelldaten(alle einzelnen bestellten Produkte, hier wird die zugehörige Bestellungs-ID gespeichert).
      In der Tabelle Bestellungen habe ich die Lieferanten ID gespeichert, und jetzt suche ich die Summe aller Preise (die in der Tabelle Bestelldaten gespeichert sind) die einer bestimmten Lieferanten ID zugeordnet sind. Etwas kompliziert(formuliert:), leichter ist das mit der Abfrage, die ich probiert habe:

      SELECT sum(t1.Preis)
      FROM Bestelldaten AS t1
      LEFT JOIN Bestellungen AS t2 ON t1.Bestellungen_ID = t2.ID
      GROUP BY t1.Preis
      HAVING t2.Lieferanten_ID = $Lieferanten_ID

      ich komme zwar von anderen SQL-Dialekten, aber vielleicht geht es so:

      SELECT be.ID,sum(bd.Preis)
            FROM Bestelldaten AS bd,Bestellungen AS be
            WHERE bd.Bestellungen_ID = be.ID
            GROUP BY bd.Bestellungen_ID

      Das kommt von undurchsichtiger Nomenklatur! So sollte es aussehen:

      SELECT be.ID,sum(bd.Preis)
             FROM Bestelldaten AS bd,Bestellungen AS be
             WHERE bd.Bestellungen_ID = be.ID
             GROUP BY be.ID

      Dabei ist dieses AS in der FROM-Klausel sicher nicht ANSI-konform. Außerdem könnte Deine Nomenklatur (Tabellen- und Spaltennamen) aussagekräftiger sein; ich mußte dreimal nachlesen, bevor ich eine Ahnung bekam - bin immer noch nicht sicher - was Bestelldaten und was Bestellungen überhaupt bedeutet. Wenn Du eine 1:n-Beziehung abbildest, könntest Du zum Beispiel mit den Präfixen "M" für Master und "C" für Child arbeiten, dann weiß jeder Fachmann sofort, was gemeint ist.

      group by Preis hab ich núr so geschrieben, da ich in Erinnerung hatte, das man da was schreiben muß, da SUM einfach mit where nicht geht.

      Die GROUP-BY-Klausel verwendet man nicht einfach so, sondern sie sagt dem SELECT, nach welchem Kriterium die Spaltenqualifizierer angewendet werden sollen; in diesem Fall also, über welche Menge von DS die Summenbildung vorgenommen werden soll.

      Die Notwendigkeit für JOINS und die HAVING-Klausel erkenne ich nicht - sofern ich die Aufgabenstellung richtig interpretiert habe.

      HTH Robert

    2. Hi Srob

      SELECT be.ID,sum(bd.Preis)
            FROM Bestelldaten AS bd,Bestellungen AS be
            WHERE bd.Bestellungen_ID = be.ID
            GROUP BY bd.Bestellungen_ID

      Die Notwendigkeit für JOINS und die HAVING-Klausel erkenne ich nicht - sofern ich die Aufgabenstellung richtig interpretiert habe.

      Einen Join hast du hier auch drin, einfach in der WHERE-Bedingung formuliert. MySQL,
      zumindest in älteren Versionen, ob es immernoch so ist weis ich nicht, mag das nicht
      besonders, es macht genau das selbe wie ein Join, war/ist jedoch beträchtlich langsamer
      bei MySQL.

      Ein kleines Dialektratespiel, wahrscheinlich Oracle, was ich weis, haben die
      solche Joins (ich glaube die heissen Thetajoin) eingeführt, könnte jedoch auch
      DB2 sein.

      Gruss Daniela

      1. Hallo Daniela,

        Einen Join hast du hier auch drin, einfach in der WHERE-Bedingung formuliert. MySQL,
        zumindest in älteren Versionen, ob es immernoch so ist weis ich nicht, mag das nicht
        besonders, es macht genau das selbe wie ein Join, war/ist jedoch beträchtlich langsamer
        bei MySQL.

        daß ich in der WHERE-Klausel implizit eine Join formuliere, ist mir bewußt. Ich wollte darlegen, daß mir die Verwendung des Keywords JOIN nicht notwendig erscheint.

        Ein kleines Dialektratespiel, wahrscheinlich Oracle, was ich weis, haben die
        solche Joins (ich glaube die heissen Thetajoin) eingeführt, könnte jedoch auch
        DB2 sein.

        Grundsätzlich gibt es ThetaJoins in beiden (und allen anderen halbwegs ANSI-konformen Dialekten auch), AFAIK bezeichnet ThetaJoin lediglich jene Untermenge aller EquiJoins, sozusagen spaltenwertvergleichender Joins, die nicht auf Gleichheit prüfen (will sagen: ThetaJoins verwenden in der Join Condition einen anderen Operator als das Gleichheitszeichen).

        Robert

      2. Hi Daniela,

        es macht genau das selbe wie ein Join, war/ist jedoch
        beträchtlich langsamer bei MySQL.

        das müßte dann aber an einem anderen execution plan liegen, den man sich mit EXPLAIN ansehen kann - oder?

        Viele Grüße
              Michael

        1. Hallo Michael,

          es macht genau das selbe wie ein Join, war/ist jedoch
          beträchtlich langsamer bei MySQL.

          das müßte dann aber an einem anderen execution plan liegen, den man
          sich mit EXPLAIN ansehen kann - oder?

          Ich glaube nicht, dass EXPLAIN den Nachteil anzeigt, auf den Daniela
          hier anspielt: aeltere MySQL-Versionen haben bei WHERE-Joins erst
          ein komplettes nxn-Produkt erstellt (karthesisches Produkt) und
          *darauf* dann den Filter laufen lassen. Inzwischen ist dieses Verhalten
          verbessert worden.

          Gruesse,
           CK

  3. Hi Andreas

    SELECT sum(t1.Preis)
    FROM Bestelldaten AS t1
    LEFT JOIN Bestellungen AS t2 ON t1.Bestellungen_ID = t2.ID
    GROUP BY t1.Preis
    HAVING t2.Lieferanten_ID = $Lieferanten_ID

    group by Preis hab ich núr so geschrieben, da ich in Erinnerung hatte, das man da was schreiben muß, da SUM einfach mit where nicht geht.

    Der group by ist in dem Fall unnötig, er ist nur nötig, wenn mehrere andere Felder
    noch da sind die verwirren können. Genauer muss über jeder Spalte die nicht eine
    Aggregatsfunktion ist in der Ausgabe, ein Group by stehen weil das DBMS sonst nicht
    wissen kann, welcher der Werte es denn für die Zeile nehmen soll (MySQL erzwingts nicht,
    sondern bringt dir einfach irgendwelche wenn du es nicht tust).

    Hat jemand eine Idee wie man das anders machen könnte, oder wo ein möglicher Fehler liegt?

    Der Having könnte die Ursache sein, und zwar wird der erst ganz am Ende auf der
    Resultatstabelle ausgeführt und da sind nur noch alle Felder drin, die auch oben
    im Select, also der Ausgabe, drin sind. Wenn du die Summe zur LieferantenID haben
    willst, dann kommt was du jetzt im Having hast in die where clause, müsste also so
    aussehen:

    Select sum(t1.Preis)
      from Bestelldaten as t1
      left join Bestellungen as t2 on t1.Bestellunge_ID = t2.ID
      where t2.lieferanten_ID = $Lieferanten_ID

    Vielleicht wäre es aber auch geschickt das Problem andersrum anzupacken,
    also die Preise dazuzujoinen.

  4. Hallo Andreas!

    SELECT sum(t1.Preis)
    FROM Bestelldaten AS t1
    LEFT JOIN Bestellungen AS t2 ON t1.Bestellungen_ID = t2.ID
    GROUP BY t1.Preis
    HAVING t2.Lieferanten_ID = $Lieferanten_ID

    group by Preis hab ich núr so geschrieben, da ich in Erinnerung hatte, das man da was schreiben muß, da SUM einfach mit where nicht geht.

    Soweit ich weiß, muß man das group nur dann nehmen, wenn man als Bedingung eine Operation hat, z.B.

    select max(laenge) as x from tab where x>10; funzt nicht. Du willst aber die Summe nur wissen, sie ist nicht Bestandteil Deiner Bedingungen.

    Schonmal so probiert?:

    SELECT sum(t1.Preis)
      FROM Bestelldaten t1
      LEFT JOIN Bestellungen t2 ON t1.Bestellungen_ID = t2.ID
      WHERE t2.Lieferanten_ID = $Lieferanten_ID;

    Hab's zwar nicht getestet, denke aber mal, daß es funzen sollte. Ansonsten kann ich nur sagen, daß es auch ohne JOIN gehen müßte:

    SELECT sum(t1.Preis)
      FROM Bestelldaten t1, Bestellungen t2
      WHERE t1.Bestellungen_ID = t2.ID AND t2.Lieferanten_ID = $Lieferanten_ID;

    Vielen Dank!
    Bitte

    Andreas

    Andreas Schigold

    http://schigold.de/spiel/

  5. Hallo!
    Danke Euch sehr für die Hilfe, jetzt klappt es! Mit Danielas Lösung ging es hervorragend. Ein kleines Problem ist aber noch aufgetaucht, und zwar habe will ich noch die Datensätze filtern, die noch nicht "bestellt" sind. Und zwar habe ich hierfür ein Feld "bestellt", Format "datetime", standardmäßig NULL. Wenn der Artikel bestellt ist, wird hier der Zeitpunkt eingetragen. Nur wie frage ich ab, ob das Feld noch leer ist? Ich hatte das mit

    WHERE bestellt = NULL

    probiert, aber das will nicht klappen, da kommt kein Datensatz zurück, obwohl da 2 kommen müßten. NONE akzeptiert MySQL gar nicht. Wie kann ich das sonst in der SQL-Abfrage machen?

    Grüße
      Andreas

    1. Hallo!
      ... standardmäßig NULL. ...

      NULL oder 0?

      WHERE bestellt = NULL

      schon mit where t1.bestellt = 0 probiert?

      Gruß
      Andreas

      1. Hi!

        NULL oder 0?

        Ich habe in phpmyadmin das Select-Feld Feld NULL ob auf NULL gestellt( beim Erstellen des Feldes). Dadurch steht in jedem Datensatz als Standardwert in phpmyadmin immer NULL in jedem Datensatz in diesem Feld. (weiß nicht ob das schlau ist?!)
        Jedenfalls passiert jetzt folgendes:

        Wenn ich gar nicht beschrenke, werden alle Datensätze ausgewählt, wenn ich = NULL schreibe, wird keiner ausgewählt, wenn ich = 0 schreibe, werden alle ausgeählt, die den Standard-Wert NULL noch nicht hatten, sondern irgendwas wie 00-00-0000 00-00-00(datetime), und genau die werden alle ausgewählt, aber der Datewnsatz, der da NULL stehen hat, wird nicht ausgewählt.

        Verstehst Du was ich meine?

        schon mit where t1.bestellt = 0 probiert?

        hatte das etwas vereinfacht für Euch

        Muß ich jetzt also doch den Wert im Feld NULL auf "Not NULL" setzten, so das überall diese Zahlenkette steht? Ich glaube auch nur PHP macht daraus diese Nullen, oder?

        Grüße
          Andreas

        1. Hi!

          das klingt ja alles mächtig kompliziert. Eigentlich sollte man wohl den Zustand 'NULL' auch entsprechend abfragen können - eigentlich! Aber vielleicht geht 'NULL' und das Datumsformat nicht so richtig? Falls die NULL nämlich nicht richtig konsequent als Symbol für "nicht belegt" behandelt wird, können da evtl. unerwünschte Effekte auftreten. Nicht falsch verstehen, ich vermute den Fehler jetzt fast bei MySQL und weniger bei Dir.

          In solchen Fällen versuche ich immer, ein wenig zu tricksen. In Deinem Fall könnte man z.B. als Standard-Datum den 1.Januar 1970 setzen oder auch auf Deinen Geburtstag oder - weiß der Geier auf was für einen Wert. Auf jeden Fall sollte das Datum als reales Ergebnis keinen Sinn ergeben.

          Mit diesem Datum belegst Du eben generell die Datensätze, die neu angelegt werden bzw. wo die Produkte, die noch nicht bestellt wurden. Dieses Datum kannst Du ja dann explizit erfragen, also

          where t1.bestellt != '01.01.1970' (oder wie die Syntax dazu jetzt auch ist, das weißt Du sicher besser - brauch ich jetzt nicht nachschauen)

          Anderer Trick, den ich in PHP schon angewendet habe, weil Du damit scheinbar auch viel arbeitest:

          strpos("abcd","bc") liefert eine 1
          strpos("abcd","ab") liefert eine 0
          strpos("abcd","xy") liefert ein false

          datentechnisch ist die 0 und false das selbe, nämlich eine 0x00 (0x steht für hex-Zahl), nur an den Datentypen (int und boolean) kann man erkennen, ob der Suchstring ganz am Anfang steht oder nicht vorkommt. Das unter www.php.net vorgeschlagene Beispiel lief aber nicht. Da habe ich einfach folgendes gemacht:

          $text = " $text"; // vornedran ein Leerzeichen oder eins, wonach man nicht als erstes sucht
          $pos = strpos($text, "Suchbegriff"); // $pos ist nun immer >= 1 bei Erfolg und 0 bei Mißerfolg

          // als letztes:
          $text = substr($text, 1); // das Leerzeichen fliegt wieder raus

          Eiegntlich finde ich das doof. In C liefert die entsprechnde Funktion eine -1 bei Mißerfolg. Aber was solls. Ich hoffe, daß mein Trick Dich weiterbringt.

          Gruß,
          Andreas

          1. Hi!
            Danke Dir! Werde mir das mal merken! Ich hab das jetzt etwas anders gemacht, und zwar habe ich in der MySQL Tabelle die das Attribut für NULL einfach auf "not NULL" gestellt, jetzt kann ich prüfen ob = 0, das war mir die sicherste und einfachste Variante!

            Grüße
              Andreas