Hallo Mathias,
Es gibt also wirklich in der Tabelle meldungen Einträge, zu denen es keine Entsprechungen in der Tabelle benutzer gibt? Also Meldungen von nicht vorhandenen Benutzern?
Regulär nicht, es wäre der Ausnahmefall. Wenn jemand eine Meldung verfasst und irgendwann später seinen Konto löschen möchte, wäre es mir lieb, den gesamten Eintrag in der Benutzertabelle löschen zu können, anstatt den Eintrag nur durch einen Flag zu deaktivieren (so eine Spalte habe ich schon, allerdings war sie für andere Fälle gedacht) und ihn dann als Karteileiche zu behalten, die nur dazu da ist, damit bei alten Kommentaren und Meldungen die Namen korrekt angezeigt werden.
Das ist Ansichtssache. Ich würde das nicht als Karteileiche bezeichnen. Immerhin gibt es für diesen Nutzer ja noch Meldungen und zu diesen Meldungen eventuell Kommentare. Das Prinzip der referentiellen Integrität würde es eigentlich gebieten, mit dem Nutzer auch die zugehörigen Meldungen und die dazu gehörenden Kommentare zu löschen. Da das, in Deinem Fall, sicherlich nicht gewollt ist, solltest Du die Nutzerdaten auch behalten. Das bringt eben den Vorteil, dass Du zur Verbindung der Tabellen meldungen und benutzer einen INNER JOIN benutzen kannst.
SELECT
meldungen.msgid,benutzer.vorname,
benutzer.nachname,Count(kommentare.msgid) AS AnzahlKommentare
FROM
(meldungen INNER JOIN benutzer ON meldungen.loginname = benutzer.loginname)
LEFT JOIN kommentare ON meldungen.msgid = kommentare.msgid
GROUP BY meldungen.msgid, benutzer.vorname, benutzer.nachname;Ist die Klammerung des ersten Joins im Allgemeinen wichtig bzw. ratsam?
Nein, die mache ich nur zur Übersicht. SQL arbeitet Joins von links nach rechts ab.
Die vielen Felder nach GROUP BY sind so nach SQL vorgeschrieben. Demnach muss in gruppierten Abfragen jedes Feld entweder zur Grupperung gehören oder Teil einer Aggregatfunktion sein.
Also sollte ich, wenn ich die Spalten
meldungen.msgid, DATE_FORMAT(meldungen.time, "%d.%m.%Y, %H:%i Uhr") AS time, DATE_FORMAT(meldungen.aenderung, "%d.%m.%Y, %H:%i Uhr") AS aenderung, meldungen.zugriff, meldungen.loginname, meldungen.meldungstitel, meldungen.meldungstext,
benutzer.vorname, benutzer.nachname,
COUNT(kommentare.msgid) AS kommentarzahl
selecte, sie unter GROUP BY entsprechend aufführen bis auf die kommentarzahl?
GROUP BY meldungen.msgid, time, aenderung, meldungen.zugriff, meldungen.loginname, meldungen.meldungstitel, meldungen.meldungstext, benutzer.vorname, benutzer.nachname
Ja, das wäre Standard-ANSI-SQL-konform. Eine gute online-Übersicht zu SQL habe ich auch noch nicht gefunden. Die Bücher, die ich hierzu habe, sind auch alle schon etwas angejahrt, weshalb ich sie nicht empfehle.
MySQL akzeptiert die Abfrage auch so, wie Du sie gruppiert hast, nimmt aber auch ohne explizite Aggregatfunktion jeweils den ersten Wert der Felder, nach denen nicht gruppiert wird.
Was hat es mit dieser Gruppierung auf sich? Mir scheint die Gruppierung nach meldungen.msgid ausreichend, wenn ich mir das Joinen veranschauliche.
Es steckt folgendes dahinter:
Du joinst z.B. meldungen, benutzer und kommentare und wählst die Felder
meldungen.msgid, meldungen.time, meldungen.aenderung, meldungen.zugriff, meldungen.loginname, meldungen.meldungstitel, meldungen.meldungstext, benutzer.vorname, benutzer.nachname, COUNT(kommentare.msgid) AS kommentarzahl
Nun kann es doch sein, dass es zu einer Meldungen mehrere Kommentare gibt. Sonst wäre ja das COUNT unsinnig. Der Join ergibt also zunächst eine Datensatzmenge, mit sovielen Datensätzen in denen die selben Daten für meldungen.msgid, meldungen.time, meldungen.aenderung, meldungen.zugriff, meldungen.loginname, meldungen.meldungstitel, meldungen.meldungstext, benutzer.vorname, benutzer.nachname stehen, wie es Kommentare zu dieser Meldung gibt. Nach meldungen.msgid wird nun gruppiert und COUNT zählt die Anzahl der gruppierten Datensätze (Ja, im Prinzip macht es genau das. Es ist nur zufällig gleich der Anzahl der kommentare.msgid ;-)). Daraus ergibt sich das endgültige Resultset, mit genau _einem_ Wert für meldungen.msgid und COUNT je Datensatz. Welcher Wert aus meldungen.time, meldungen.aenderung, meldungen.zugriff, meldungen.loginname, meldungen.meldungstitel, meldungen.meldungstext, benutzer.vorname, benutzer.nachname soll aber nun in den Datensätzen dieses Resultsets stehen? Gibt es z.B. drei gleiche meldungen.msgid, dann stehen jeweils drei Datensätze mit Werten für die andern Felder zur Verfügung. In Deinem Fall sind diese Werte alle identisch, weshalb Du auch einfach nach allen gruppieren kannst. Das muss aber nicht so sein. Deshalb ist es vorgeschrieben, dass man in der SQL-Abfrage festlegt, welche Werte der nicht gruppierten Felder man haben möchte. Das geschieht dann mit den Aggregatfunktionen First() bzw. Last(). MySQL nimmt eben automatisch den ersten (First()) der drei Datensätze als Quelle.
viele Grüße
Axel