Select Max(..) von MySQL 4.1 nach 4.0 portieren
Peter A.
- datenbank
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
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
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.
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
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.
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
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.
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