Gerry: Mysql -> Having

Hallo zusammen,
Ich habe eine Frage bezüglich MySQL.
In einer SQL Abfrage soll aus einer Gruppierung (GROUP BY CustomerId, Product) immer der neuste (IssueDate) Datensatz angezeigt werden.
Ich hab es so probiert:

"SELECT * FROM Products GROUP BY CustomerId, Product HAVING MAX(IssueDate)"

Funktioniert nicht... Habe da als Beispiel mehrere Datensätze kreiirt. Jeder ist ein Jahr höher. Trozdem wird der 2006 anstatt 2007 angezeigt. Aber nie 2005 oder 2004, was somit auch korrekt ist.
Wieso aber 2006 statt 2007, geht das mit dem HAVING anders? Oder ist es gar illegal?

Besten Dank & Gruss
vom Gerry

PS: Ich habe die INT Werte verglichen, und das 2007 ist tatsächlich > 2006 >2005 >2004... Somit, sollte HAVING funktionieren... :-S *confused*

  1. Hello,

    Ich hab es so probiert:
    "SELECT * FROM Products GROUP BY CustomerId, Product HAVING MAX(IssueDate)"

    keine Sorge, dein hiesiger Fehler unterläuft wohl allen, die GROUP BY auf MySQL lernen...
    Eine SELECT Klausel in einer gruppierten Abfrage darf nur zwei Typen von Spalten enthalten:

    1. gelistet unter GROUP BY
    2. mittels Aggregatfunktion (MAX, SUM, ...) aus der Gruppe auf einen Wert aggregiert
      --> logischer Erklärung: es ist nicht klar, welcher Wert für eine andere Spalte dort hingehört - selbst wenn du als Mensch argumentierst, "ich gruppiere Nutzer nach Sozialversicherungsnummer, da ist doch der Name immer gleich", das ist für die Datenbank nicht ersichtlich. Leider unterlässt MySQL diese Prüfung und liefert dir keinen Fehler. Das sollte dich nicht davon abhalten, sauber nach den SQL-Regeln zu arbeiten.

    Wieso aber 2006 statt 2007, geht das mit dem HAVING anders? Oder ist es gar illegal?

    IMHO jein, also syntaktisch erlaubt aber sinnfrei. HAVING MAX(IssueDate) heißt nur in menschliche Sprache übersetzt "und hat das maximale IssueDate" - für die Datenbank entbehrt der Vergleich einer Grundlage, die will wissen welches MAX-Date du suchst. Entweder du lässt HAVING weg, selektierst MAX(IssueDate) mit, sortierst danach und verwendest LIMIT, oder du wendest ein weiteres SUBSELECT an
    SELECT ..., MAX(IssueDate) m_i_dt
    HAVING m_i_dt = (SELECT ... )

    MfG
    Rouven

    --
    -------------------
    There's no such thing as a free lunch  --  Milton Friedman
    1. Hallo Rouven,

      [...] oder du wendest ein weiteres SUBSELECT an

      als Ergänzung zu Deiner schönen Erklärung noch ein Link zu einem Archivposting
      von mir, das korrelierte Subselects an einem Beispiel erläutert.

      Freundliche Grüße

      Vinzenz

    2. yo,

      du lässt HAVING weg, selektierst MAX(IssueDate) mit, sortierst danach und verwendest LIMIT

      von dieser "lösung" ist immer abzuraten, es sei den im speziallfall, dass datensätze mit gleichen MAX(IssueDate) nicht alle angezeigt werden sollen, sondern nur einer von den mehreren möglichen. Aber auch dann würde ich aufgrund der besseren lesbarkeit eine andere lösung bevorzugen.

      Ilja

  2. Moin!

    Ich habe eine Frage bezüglich MySQL.

    Du bist in die wohlbekannte MySQL-Falle getappt, die beim Gruppieren bereitgehalten wird.

    "SELECT * FROM Products GROUP BY CustomerId, Product HAVING MAX(IssueDate)"

    SELECT * funktioniert zusammen mit GROUP BY nicht. MySQL liefert leider keinen Fehler, weil es diese Abfrage intern "optimiert", diese Optimierung ist allerdings nur in seltenen Fällen gültig.

    Faustregel: Wann immer GROUP BY benutzt wird, dürfen in der Liste der selektierten Spalten nur auftauchen:
    1. alle in GROUP BY auch aufgelisteten Spalten
    2. alle anderen Spalten nur in Verbindung mit Aggregatfunktionen wie z.B. MAX, COUNT, AVG, SUM, etc...

    Folgt also für dich: Du kannst mit deiner Abfrage derzeit nur CustomerID und Product ermitteln, weil nix anderes im GROUP BY steht. Logischerweise kannst du auch noch das Maximum der Spalte IssueDate in die Selektion aufnehmen. Aber mehr Infos kriegst du erstmal nicht.

    Führe diese Abfrage mal durch in einem Admin-Tool (wie PHPMyAdmin). Dann siehst du die Liste.

    Jetzt geht es ans Filtern unerwünschter Einträge. Mit WHERE filterst du, welche Datensätze in die Gruppierung eingehen sollen. Mit HAVING filterst du, welche gruppierten Ergebnisse ins Endergebnis einfließen sollen.

    Funktioniert nicht... Habe da als Beispiel mehrere Datensätze kreiirt. Jeder ist ein Jahr höher. Trozdem wird der 2006 anstatt 2007 angezeigt. Aber nie 2005 oder 2004, was somit auch korrekt ist.
    Wieso aber 2006 statt 2007, geht das mit dem HAVING anders? Oder ist es gar illegal?

    Ohne Beispieldaten dürfte es schwierig werden, dein Problem nachzuvollziehen. Welche Bedeutung haben die Spalten? Was willst du ermitteln?

    - Sven Rautenberg

    --
    "Love your nation - respect the others."
    1. Moin!

      Moin!

      Also ich habe nun 1. und 2. befolgt.
      Ich kriege aber nicht den aktuellen, neusten Datensatz, sondern irgendeiner der per Zufall da in der GROUP BY ausselektiert wurde :-S (Keine Ahnung wie ich das beeinflussen soll.)

      Wenn ich nun aber zusätzlich nebst den gebrauchten Felder ein MAX(IssueDate) as maxdate ausselektiere und dieses ausgebe, würde ich das KORREKTE Datum (Datensatz) dieser Gruppierung jeweils bekommen.

      Wie kann ich also, dieses MAXDATE abfragen; Dass somit die Gruppierung GENAU den Datensatz nimmt, wo dieses MAXDATE dabei ist?!
      Mit WHERE oder HAVING habe ich nun eine Zeit lang rumgespielt, hab aber das Ergebnis nicht so gekriegt wie ich wollte...

      Gruss Gerry

      Hier mein Query, das sich fast alle 5min ändert ;-)

      SELECT MAX(IssueDate) AS maxda, tblCustomer.Id AS CustomerId, Product, Version FROM tblProduct LEFT JOIN tblCustomer ON Fk_CustomerId = tblCustomer.Id WHERE 1 GROUP BY CustomerId, Product, Version

      Hab auch schon versucht HAVING(maxda) oder WHERE IssueDate=maxda und und... habe momentan echt Bahnhof...

      Danke euch!

      1. ...Er sortiert nach der Id anstatt dem Datum innerhalb der Gruppe. Dies soll geändert werden... Innerhalb der Gruppe, dies kann man mit WHERE machen, soweit ich verstanden habe.

        Kann mir dazu bitte jemand ein solches WHERE IssueDate=(Select MAX(IssueDate) beispiel geben.
        Ich kriegs nicht hin :(

        Danke

      2. Hallo Gerry,

        Ich kriege aber nicht den aktuellen, neusten Datensatz, sondern irgendeiner der per Zufall da in der GROUP BY ausselektiert wurde :-S (Keine Ahnung wie ich das beeinflussen soll.)

        Gar nicht, das kannst Du nicht beeinflussen. Das Ergebnis ist zufällig.
        Jedes andere DBMS (Datenbankmanagementsystem) als MySQL, das ich kenne,
        gibt Dir sowieso kein Ergebnis, sondern eine Fehlermeldung zurück.

        Wie kann ich also, dieses MAXDATE abfragen; Dass somit die Gruppierung GENAU den Datensatz nimmt, wo dieses MAXDATE dabei ist?!

        Mit einem korrelierten Subselect.

        SELECT MAX(IssueDate) AS maxda, tblCustomer.Id AS CustomerId, Product, Version FROM tblProduct LEFT JOIN tblCustomer ON Fk_CustomerId = tblCustomer.Id WHERE 1 GROUP BY CustomerId, Product, Version

        Möchtest Du also das letzte Ausgabedatum je Produkt und Kunde haben?

          
        SELECT                        -- Nimm  
            p.IssueDate,              -- das Ausgabedatum  
            c.Id AS CustomerId,       -- die KundenID  
            p.Product,                -- das Produkt  
            p.Version,                -- die Version  
        FROM tblProduct p             -- aus der Tabelle Produkte  
        /*  
           Wenn Du eh' nach Kunden gruppierst, warum nimmst Du dann einen  
           LEFT JOIN. Ich würde eher einen INNER JOIN erwarten.  
           Oder interessiert dich auch die letzte Ausgabe eines Produkts,  
           zu dem _kein_ Kunde eingetragen wurde?  
        */  
        LEFT JOIN tblCustomer c          -- und der Tabelle Kunden  
        ON p.Fk_CustomerId = c.Id        -- die über diese beiden Spalten verknüpft sind  
        WHERE p.IssueDate = (            -- wobei jeweils nur das  
        WHERE zeit = (  
            SELECT MAX(p2.IssueDate)     -- das neueste Datum  
            FROM tblProduct p2  
            WHERE p.Product = p2.Product -- je Produkt  
                AND p.Fk_CustomerId = p2.Fk_CustomerId  -- und Kunde interessiert.  
        )
        

        ganz analog zu meinem bereits verlinkten Beispiel im Archiv.

        Freundliche Grüße

        Vinzenz

        1. Hallo Gerry,

          Tach auch :-)

          Möchtest Du also das letzte Ausgabedatum je Produkt und Kunde haben?

          Jupp...
          Hey, ich möchte dir mal fett Danke sagen für dein Beispiel!!! Es läuft genau so wie es sollte. Ich habe auch noch die Joins ausgewechselt, weil du recht hast ;-)

          Besten Dank für deine Super Hilfe & Gruss
            Gerry