Peter A.: Select Max(..) von MySQL 4.1 nach 4.0 portieren

Hallo allerseits,

Ich habe mir mühsam folgendes  Statement zusammen gebastelt,
welches mir von jeder News-Kategorie den jeweils neusten
News-Eintrag zurückliefert. Das ganze läuft ab Version 4.1.x

(vereinfacht)
SELECT DISTINCT
  modul.id, modul.category_id, modul.topic,
  categories.category,
FROM
  nboard modul,
  nboard_categories categories
WHERE
  categories.id = modul.category_id
AND
  modul.created = (
    SELECT MAX( tab1.created )
    FROM nboard tab1
    WHERE tab1.category_id = modul.category_id
  )
ORDER BY
  modul.category_id
LIMIT 0 , 30

Jetzt muss ich allerdings mein Programm auf MySQL 4.0 downgraden.
Und hierbei tritt das Problem auf, dass er das Subselect
"SELECT MAX(..)"
nicht verträgt. Nur habe ich keinen blassen Schimmer, wodurch
sich das unter der 4er Version ersetzen lässt.
Kommt das evtl. einem OuterJoin gleich?

Könnte mit vllt. einer von euch ein paar Tipps geben?

Bin für jede Hilfe dankbar.

mfg
Peter

  1. yo,

    Kommt das evtl. einem OuterJoin gleich?

    nein, mit einem OUTER JOIN alleine wird das meiner meinung nach nicht gehen. in aller regel sind dann wohl zwei abfrqagen fällig oder aber du kannst einen weg über GROUP BY und HAVING finden. der geht aber nur in wenigen fällen und ist somit nicht immer anzuwenden. dafür müsste man schon mehr übder das deten-desgin wissen, da es sehr spezifisch wird.

    Ilja

    1. Hallo Ilja,

      Danke für deine Antwort.
      Also mein Datenmodell sieht grob aus wie folgt:

      Tabelle 1 (Content):
       nboard: id | category_id | topic | created

      Tabelle 2 (Categories):
       nboard_categories: id | category

      Ich habe es jetzt mal anhand der MySQL-Docu mittels
      GROUP BY und HEAVING versucht.
      Hier ein paar Beispiele:

      ==1==
      SELECT
       max( modul.created ) AS 'maxcreated',
       modul.topic,
       cats.category
      FROM
       nboard modul
      LEFT JOIN
       nboard_categories cats ON modul.category_id = cats.id
      GROUP BY
       modul.category_id
      HEAVING
       modul.created = maxcreated     (modul.created logischerweise unbekannt)

      ==2==
      select
        max( modul.created ) AS 'created',
        modul.topic,
        cats.category
      from
        nboard modul,
        nboard_categories cats
      where
        modul.category_id = cats.id
      group by
        modul.category_id
      ORDER BY
        modul.category_id ASC,
        modul.created DESC

      ==3==
      SELECT
       modul.created AS 'curcreated',
       max( modul.created ) AS 'maxcreated',
       modul.topic,
       cats.category
      FROM
       nboard modul
      LEFT JOIN
       nboard_categories cats ON modul.category_id = cats.id
      GROUP BY
       cats.category
      HAVING
       curcreated = maxcreated

      Die Daten, die bei Nummer 2 ankommen, sehen teils auch
      schon recht gut aus. In dem Fall bekomme ich zB für jede
      Kategorie das jeweils aktuellste Created-Datum. Als weitere
      Spalte die Cat-Id, und als dritte Spalte den Topic. Der
      allerdings verweist fälschlicherweise immer auf den ersten
      Eintrag einer jeden Kategorie (der mit der niedrigsten ID).
      Hinsichtlich der Zuordnung zwischen der Category-Id und dem
      Created-Datum scheint widerum alles OK zu sein.

      Daraus schließe ich, dass wohl keine direkte Verbindung
      zwischen den gefetchten Daten eines Datensatzes besteht.

      Entweder müsste ich Die Verbindung erstmal herstellen, oder
      ich müsste es auch noch irgendwie schaffen den neusten topic
      auszulesen. Allerdings bekäme ich an dieser Stelle dann
      Probleme, was das Auslesen weiterer Parameter der Modul-
      Tabelle (hier ausgeblendend) anbelangt.

      Was meinst Du dazu?

      MfG
      Peter A.

      1. yo,

        Ich habe es jetzt mal anhand der MySQL-Docu mittels
        GROUP BY und HEAVING versucht.

        nun, du bist dabei auf den gemeinen "group by mysql" effekt reingefallen. das ist keine schande, sondern passiert wohl den aller meisten. ursache dafür ist, dass mysql keine fehlermeldung mehr bei falscher verwendung von group by anzeigt, um sich sortierungen zu sparen. die nachteile der falsche verwendung überwiegen aber deutlich den versuch performance zu gewinnen und wurde hier schon mehrfach kritisiert. mal abwarten, wann mysql wieder einen rückzieher macht.

        grundsätzlich gilt, dass man über alle spalten auch gruppieren muss, die man in der ausgabe anzeigen will, es sei den es handelt sich um aggregat-funktionen.
        warum das so ist, wird anhand deiner beispiele deutlich, weil dann mysql zufällig sich für eines der vielen unterschiedlichen werten innerhalb einer gruppierung entscheiden muss.

        was die abfrage mit group by betrifft, so kannst du meiner meinung nach maximal folgende spalten mit in die gruppierung rein nehmen:

        GROUP BY nboard_categories.id, nboard_categories.category

        und dann über die ausgabe:

        SELECT nboard_categories.id, nboard_categories.category, MAX(nboard.created)

        mehr kannst du mit einer abfrage wohl ohne unterabfragen nicht rausholen, weil es sonst die gewünschte gruppierung durcheinander bringen würde und bei der ausgabe der oben gennanten gründe nur diese auch wieder angezeigt werden können.

        Ilja

        1. Hallo Ilja,

          da ich bereits bei der Archiv-Suche hinsichtlich dieser Problemstellung
          viele Posting von Dir gefunden habe, gehe ich mal davon aus, dass Du
          dich in diesem Bereich gut auskennst, und ich wohl leider hinzunehmen
          habe, dass es anscheinend wirklich nicht über eine einzige Query
          zu realisieren ist. Grr, den ganzen Samstag umsonst gearbeitet.

          Ich habe mir jetzt folgendes zusammengebastelt:

          SELECT nboard_categories.id, nboard_categories.category, MAX(nboard.created) , MAX(nboard.id)
          FROM nboard_categories, nboard
          WHERE nboard_categories.id=nboard.category_id
          GROUP BY nboard_categories.id, nboard_categories.category

          So dass ich zumindest direkt die ID des Datensatzes im Resultset
          habe. Kann es vorkommen, dass bei einer auto_increment ID-Spalte
          nicht zwangsweise die höchste ID auch den aktuellsten TimeStamp
          besitzt? somit wäre nämlich auch dieser Lösungsweg nicht wirklich
          zufriedenstellend.

          Zudem, wie kann ich überprüfen, ob die aktuelle MySQL-Version
          SUBSELECTS unterstützt, so dass ich zumindest eine Weiche
          programmieren kann? Oder müsste ich hierfür einfach den
          auftretenden Fehler (bei Nicht-Unterstützung) abfangen und
          schließlich dort die alternative Lösung implementieren?

          Vielen Dank für Deine Hilfsbereitschaft.

          Peter A.

          1. yo,

            So dass ich zumindest direkt die ID des Datensatzes im Resultset
            habe. Kann es vorkommen, dass bei einer auto_increment ID-Spalte
            nicht zwangsweise die höchste ID auch den aktuellsten TimeStamp
            besitzt?

            grundsätzlich sind die beiden MAX() aggregat-funktionen, die du in deiner query eingesetzt hast, unabhängig voneinander. das heisst es kann durchaus vorkommen, dass die höchste id nicht dem höchsten datum entspricht. sicherlich wird es meistens übereinstimmen, aber es ist eben keine 100% gültigkeit.

            Zudem, wie kann ich überprüfen, ob die aktuelle MySQL-Version
            SUBSELECTS unterstützt, so dass ich zumindest eine Weiche
            programmieren kann? Oder müsste ich hierfür einfach den
            auftretenden Fehler (bei Nicht-Unterstützung) abfangen und
            schließlich dort die alternative Lösung implementieren?

            ich habe keine ahnung, welche programmsprache du benutzt und ob es da möglich ist. aber wenn du schon über die möglichkeit einer programmiersprache verfügst, warum machst du es dann nicht einfach mit zwei abfragen wasserdicht ? das wäre meine empfehlung, da zwei abfragen oftmals der bessere weg sind, als zuviel energie zu verwenden, es mit einer abfrage zu lösen. keep it simple.

            ich werde heute noch mal mit meiner freundin spazieren gehen und mir noch mal die abfrage durch den kopf gehen lassen. vielleicht ist ja noch was mit einem self join zu machen. aber ich befürchte, zumindestens ich komme bis jetzt dabei noch nicht auf eine lösung. vielleicht weiß jemand mehr rat.

            Ilja

            1. Hi,

              also ich habe das nun wie folgt umgesetzt:

              Vor der eigentlichen Abfrage bastel ich mit eine erweiterte
              WHERE-Condition mit den jeweils höchstem Timestamp einer
              Category zusammen:

              $sg = "SELECT distinct max(created) AS mc FROM $modul modul  "
                    "GROUP BY category_id ORDER BY category_id ASC";
              $qg = mysql_query($sg);
              while($maxcreated=mysql_fetch_array($qg) )
                $whereString .= "OR modul.created=".$maxcreated['mc']." ";

              und füge das dann anschließend dem eigentlichem Query an.

              Klappt ganz gut, und bei max. 20 Kategorien m.E. auch recht
              akzeptabel.

              Dir nochmal tausend Dank für Deine ausführliche Hilfe.

              MfG
              Peter A.

              1. yo,

                Vor der eigentlichen Abfrage bastel ich mit eine erweiterte
                WHERE-Condition mit den jeweils höchstem Timestamp einer
                Category zusammen:

                ich hoffe, du schauchst hier noch einmal vorbei, weil es so nicht geht. du kannst nicht über das datum gehen, weil das nicht eindeutig ist. so kann ein datum das höchste in einer kategorie sein, das in einer anderen auch vorkommt, dort aber nicht das höchste ist.

                was du tun kannst, ist über die id's der zu gehen, da diese wirklich eindeutig (PK) sind oder aber du nimmst die kategorie_id mit rein, was wohl einfacher sein wird. Distinct und Order by sind dabei überflüssig.

                SELECT category_id, MAX(datum) AS date
                FROM tabelle
                GROUP BY category_id

                die zweite query musst du dann ähnlich zusammenbauen, wobei die category_id und das date mit dem operator AND verbunden sein müssen.

                Ilja