Vinzenz Mai: GROUP BY + COUNT ...

Beitrag lesen

Hallo

Was ist ein gutes Tabellendesign dafür? Ich speichere in einer Tabelle die Einträge ab. In der anderen Tabelle sind die Kommentare gespeichert. In der Tabelle für die Kommentare habe ich auch gespeichert, auf welchen Blogeintrag sich die Kommentare beziehen. Ist das so richtig, oder nicht?

das hört sich schon einmal gut an. Noch besser wäre es gewesen, wenn Du Deine Tabellenstruktur gepostet hättest.

und mit MySQL 4.1
Ich habe MySQL - 5.0.21.

Das ist erfreulich.

sollte dies einfach mit einem Subquery zu lösen sein.
Was ist ein Subquery?

Eine Unterabfrage :-)
sub kommt aus dem Lateinischen und bedeutet "unter".
quaerere kommt ebenfalls aus dem Lateinischen und bedeutet "fragen".

Auch das Englische bedient sich reichlich bei den alten Sprachen :-)

Mit Hilfe der GROUP-BY-Klausel und der Aggregatsfunktion COUNT() sollte Dein Problem lösbar sein.

Ok, ich lese mir mal das Manual dazu durch.

Nehmen wir einfach an, Du habest eine Tabelle blog mit folgenden Daten:

blog_id | titel    | eintrag       | zeitstempel
------------------------------------------------------
      1 | Erster   | ...           | 2007-02-01 14:37
      2 | Zweiter  | ...           | 2007-02-12 17:15
      3 | Dritter  | ...           | 2007-02-25 05:12
      4 | Vierter  | ...           | 2007-03-15 11:11
      5 | Fünfter  | ...           | 2007-04-01 20:44
      6 | Sechster | ...           | 2007-04-26 19:59

und eine Tabelle kommentare wie folgt

kommentar_id | blog_id | weitere Spalten, für Dein Problem irrelevant
---------------------------------------------------------------------
           1 |       2 |
           2 |       2 |
           3 |       4 |
           4 |       2 |
           5 |       3 |
           6 |       1 |
           7 |       4 |
           8 |       6 |
           9 |       2 |

1. Schritt: Lasse Dir zu den Blogeinträgen die Kommentare anzeigen:

SELECT                   -- Gib mir  
    b.blog_id            -- die id des Blogeintrags,  
    b.titel,             -- den Titel,  
    b.zeitstempel,       -- den Erstellungszeitpunkt,  
    k.blog_id            -- und die dazu passenden Einträge in der Kommentartabelle  
FROM blog b              -- aus der Tabelle blog  
LEFT JOIN kommentare k   -- die mit der Tabelle kommentare  
ON b.blog_id = k.blog_id -- über die Spalte blog_id verknüpft ist

Der LEFT JOIN sorgt dafür, dass alle Einträge in der Tabelle blog berücksichtigt werden, auch wenn es keine Kommentare zum Blogeintrag gibt.

Ergebnis:

b.blog_id | titel    | zeitstempel         | k.blog_id
------------------------------------------------------
       1  | Erster   | 2007-02-01 14:37:00 |        1
       2  | Zweiter  | 2007-02-12 17:15:00 |        2
       2  | Zweiter  | 2007-02-12 17:15:00 |        2
       2  | Zweiter  | 2007-02-12 17:15:00 |        2
       2  | Zweiter  | 2007-02-12 17:15:00 |        2
       3  | Dritter  | 2007-02-25 05:12:00 |        3
       4  | Vierter  | 2007-03-15 11:11:00 |        4
       4  | Vierter  | 2007-03-15 11:11:00 |        4
       5  | Fünfter  | 2007-04-01 20:44:00 |     NULL
       6  | Sechster | 2007-04-26 19:59:00 |        6

Du siehst: Obwohl es zum fünften Blogeintrag noch keinen Kommentar gibt,
ist auch dieser Datensatz vorhanden. In der zur id korrespondierenden
Spalte aus der Tabelle kommentare steht dort der spezielle Wert NULL.
Dieser bedeutet "keine Daten vorhanden". NULL ist etwas ganz anderes
als die Zahl 0 oder eine leere Zeichenkette.

Um nun Deine Anzahl zu bekommen musst Du lediglich nach den Spalten

b.blog_id, titel und zeitstempel

gruppieren und die Anzahl der Einträge in der Spalte k.blog_id zählen:

SELECT                         -- Gib mir  
    b.blog_id                  -- die id des Blogeintrags,  
    b.titel,                   -- den Titel,  
    b.zeitstempel,             -- den Erstellungszeitpunkt,  
    COUNT(k.blog_id) AS anzahl -- und die Anzahl der Kommentare  
FROM blog b                    -- aus der Tabelle blog  
LEFT JOIN kommentare k         -- die mit der Tabelle kommentare  
ON b.blog_id = k.blog_id       -- über die Spalte blog_id verknüpft ist  
GROUP BY  
    b.blog_id,                 -- gruppiere nach den Spalten, auf die  
    b.titel,                   -- keine Aggregatsfunktion wie COUNT,  
    b.zeitstempel              -- MAX, MIN, o.ä. angewandt wird.  

mit dem Ergebnis:

b.blog_id | titel    | zeitstempel         | anzahl
------------------------------------------------------
       1  | Erster   | 2007-02-01 14:37:00 |        1
       2  | Zweiter  | 2007-02-12 17:15:00 |        4
       3  | Dritter  | 2007-02-25 05:12:00 |        1
       4  | Vierter  | 2007-03-15 11:11:00 |        2
       5  | Fünfter  | 2007-04-01 20:44:00 |        0
       6  | Sechster | 2007-04-26 19:59:00 |        1

Lies bitte im Handbuch nach, wie die Funktion COUNT() mit NULL-Werten
umgeht. Beachte bitte den Unterschied zu COUNT(*).

Im letzten Schritt möchtest Du die fünf neuesten haben, d.h. Du
sortierst absteigend nach der Spalte zeitstempel und limitierst das
Ergebnis auf fünf Datensätze:

SELECT                         -- Gib mir  
    b.blog_id                  -- die id des Blogeintrags,  
    b.titel,                   -- den Titel,  
    b.zeitstempel,             -- den Erstellungszeitpunkt,  
    COUNT(k.blog_id) AS anzahl -- und die Anzahl der Kommentare  
FROM blog b                    -- aus der Tabelle blog  
LEFT JOIN kommentare k         -- die mit der Tabelle kommentare  
ON b.blog_id = k.blog_id       -- über die Spalte blog_id verknüpft ist  
GROUP BY  
    b.blog_id,                 -- in Standard-SQL ist nach allen Spalten zu  
    b.titel,                   -- gruppieren, auf die keine Aggregatsfunktion  
    b.zeitstempel              -- angewendet wird  
ORDER BY  
    b.zeitstempel DESC         -- sortiere absteigend nach Erstellungszeitpunkt  
LIMIT 5                        -- und begrenze das Ergebnis auf die ersten  
                               -- fünf Einträge der Ergebnismenge  

mit dem Ergebnis

b.blog_id | titel    | zeitstempel         | anzahl
------------------------------------------------------
       6  | Sechster | 2007-04-26 19:59:00 |        1
       5  | Fünfter  | 2007-04-01 20:44:00 |        0
       4  | Vierter  | 2007-03-15 11:11:00 |        2
       3  | Dritter  | 2007-02-25 05:12:00 |        1
       2  | Zweiter  | 2007-02-12 17:15:00 |        4

Es ist problemlos möglich, noch weitere Spalten aus der Tabelle blog mit
in die Abfrage aufzunehmen. Bitte gewöhne Dir von Anfang an an, nach allen
diesen Spalten zu gruppieren - auch wenn dies in MySQL nicht notwendig wäre.
Alle anderen mir bekannten Datenbankmanagementsysteme geben Dir ansonsten
eine Fehlermeldung aber kein Ergebnis zurück.

Dies war die Lösung mit einem LEFT JOIN, sowas funktioniert auch in älteren
MySQL-Versionen. Das Ergebnis kannst Du wie gehabt mit PHP ausgeben. Du
siehst, dass Du keine Abfragen innerhalb der Schleife absetzen musst, um
das von Dir gewünschte Resultat zu erhalten.

Freundliche Grüße

Vinzenz

PS: Viel Erfolg bei Deiner Prüfung