Vinzenz Mai: MySQL: GROUP-BY und Ergebnisse in nicht gruppierten Spalten

Beitrag lesen

Hallo,

zunächst mal danke für die Blumen, aber ...

Ich blicke zwar noch nicht ganz durch, "warum",

... aus diesem Grund ist das nur die halbe Miete.

aber dein Statement bringt nun TATSÄCHLICH das gewünschte Ergebnis auf BEIDEN MySQL Versionen.
Respekt, da kennt sich wer WIRKLICH aus. :-)

Mein Ziel ist es eigentlich, dass der Fragesteller versteht, wie eine Lösung funktioniert, damit dieser in Zukunft es allein hinbekommt.

Als grundsätzliche Lektüre empfehle ich Dir die JOIN-Artikel in SELFHTML Aktuell:
Einführung Joins
Fortgeschrittene Jointechniken

Der Weg zu der Lösung Deiner Problemstellung in Einzelschritten. Ein paar weniger wichtige Spalten habe ich der Übersichtlichkeit halber weggelassen.

Zuerst die Beispieltabellen:

1. prb_user

login_name  first_name  last_name
---------------------------------
abel        Abel        Mensch
adam        Adam        Mensch
eva         Eva         Mensch
kain        Kain        Mensch
seth        Seth        Mensch

2. prb_request
   request_number ist mit rn abgekürzt, hierarchy_location mit hl :-)

rn  hl      request                      author
-----------------------------------------------
1   1       Staub                        adam
2   1.1     Rippe                        eva
3   1.2     Die erste Zeugung            kain
4   1.3     Das Wunder Mensch            seth
5   2       Mmh, ein Apfel               eva
6   2.1     Lecker                       adam
7   2.1.1   Ohne den gäbs mich nicht     kain
8   2.1.2   Mich auch nicht              abel
9   3       Vegetarier leben gefährlich  abel

3. prb_request_category
   request_number ist mit rn abgekürzt

rn  category
------------------
1   Erschaffung
5   Nahrung
9   Risikofaktoren

Es folgt der Zusammenbau der SELECT-Statements:

a) Ganz einfacher Beginn.
   Auswählen dreier Spalten aus der Tabelle prb_request

SELECT  
  pr1.request_number rn,      -- Aliasnamen für kürzere Schreibweisen  
  pr1.hierarchy_location hl,  
  pr1.request  
FROM prb_request pr1

Ergebnis:

rn  hl     request
--------------------------------------
1   1      Staub
2   1.1    Rippe
3   1.2    Die erste Zeugung
4   1.3    Das Wunder Mensch
5   2      Mmh, ein Apfel
6   2.1    Lecker
7   2.1.1  Ohne den gäbs mich nicht
8   2.1.2  Mich auch nicht
9   3      Vegetarier leben gefährlich

b) Verknüpfe die Tabelle prb_request mit einem INNER JOIN mit der Tabelle
   prb_request, damit wir ausschließlich die Beiträge der ersten Ebene erhalten.
   Wir erinnern uns:

Wieviele Einträge zu einem Eintrag in prb_request kann es in prb_request_category geben? Einen oder mehrere?
Nur einen, ein Thema kann maximal nur in einer Kategorie aufgeführt sein.

SELECT  
  pr1.request_number rn,  
  pr1.hierarchy_location hl,  
  pr1.request,  
  prc.category  
FROM prb_request pr1  
INNER JOIN prb_request_category prc  
ON pr1.request_number = prc_request_number

führt zu dem Ergebnis

rn  hl  request                      category
------------------------------------------------
1   1   Staub                        Erschaffung
5   2   Mmh, ein Apfel               Nahrung
9   3   Vegetarier leben gefährlich  Risikofaktoren"

c) Im nächsten Schritt holen wir uns die Detailinformationen zum Autor,
   dazu dient ein INNER JOIN mit der Tabelle prb_user:

SELECT  
  pr1.request_number rn,  
  pr1.hierarchy_location hl,  
  pr1.request,  
  pr1.author,  
  prc.category,  
  pu.last_name l_name,  
  pu.first_name f_name  
FROM prb_request pr1  
INNER JOIN prb_request_category prc  
ON pr1.request_number = prc.request_number  
INNER JOIN prb_user pu  
ON pr1.author = pu.login_name

Ergebnis:

rn  hl  request                      author  category        l_name  f_name
---------------------------------------------------------------------------
1   1   Staub                        adam    Erschaffung     Mensch  Adam
5   2   Mmh, ein Apfel               eva     Nahrung         Mensch  Eva
9   3   Vegetarier leben gefährlich  abel    Risikofaktoren  Mensch  Abel

d) Nun kommen wir zum Selfjoin, der uns das Zählen
   der Antworten ermöglichen wird. Jeder Eintrag unseres Zwischenergebnisses wird mit dem
   jedem Eintrag verknüpft, der diesen Eintrag als "Starteintrag" hat. Das ganze ist etwas
   trickreich, daher ausführlich der Zwischenschritt:

SELECT  
    request_number AS rn,  
    hierarchy_location AS hl,  
    -- Bestimme die Position des ersten Punktes in hierarchy_location:  
    [link:http://dev.mysql.com/doc/refman/4.1/en/string-functions.html#id3068426@title=LOCATE]('.', pr2.hierarchy_location) AS erster_punkt,  
    -- Wenn es einen solchen Punkt gibt  
    --     Dann gib den Teilstring bis zum ersten Punkt zurück,  
    --     d.h. den Anteil, der den Startbeitrag angibt  
    -- Sonst  
    --     muss es ein Beitrag erster Ebene sein, d.h. selbst Startbeitrag  
    --     Gib dessen hierarchy_location zurück  
    [link:http://dev.mysql.com/doc/refman/4.1/en/control-flow-functions.html#id3065568@title=IF] (LOCATE('.', pr2.hierarchy_location),  
        [link:http://dev.mysql.com/doc/refman/4.1/en/string-functions.html#id3068149@title=LEFT](pr2.hierarchy_location, LOCATE('.', pr2.hierarchy_location) - 1),  
        pr2.hierarchy_location) AS Beitrag  
FROM prb_request pr2

rn  hl     erster_punkt  Beitrag
--------------------------------
1   1      0             1
2   1.1    2             1
3   1.2    2             1
4   1.3    2             1
5   2      0             2
6   2.1    2             2
7   2.1.1  2             2
8   2.1.2  2             2
9   3      0             3

Für Beiträge der obersten Ebene liefert LOCATE den Wert 0 zurück, dies entspricht in MySQL
dem boolschen Wert false. D.h. IF liefert mit Hilfe von LEFT stets
die hierarchy_location des zugehörigen Startbeitrags zurück. Als JOIN-Bedingung nutzen wir
daher die Gleichheit des hierarchy_location des Startbeitrags und der berechneten Spalte
"Beitrag".

e) In Aktion:

SELECT  
  pr1.request_number rn,  
  pr1.hierarchy_location hl,  
  pr1.request,  
  pr1.author,  
  prc.category,  
  pu.last_name l_name,  
  pu.first_name f_name,  
  IF (LOCATE('.', pr2.hierarchy_location),  
    LEFT(pr2.hierarchy_location, LOCATE('.', pr2.hierarchy_location) - 1),  
    pr2.hierarchy_location) AS Beitrag  
FROM prb_request pr1  
INNER JOIN prb_request_category prc  
ON pr1.request_number = prc.request_number  
INNER JOIN prb_user pu  
ON pr1.author = pu.login_name  
INNER JOIN prb_request pr2  
ON pr1.hierarchy_location =  
  (IF (LOCATE('.', pr2.hierarchy_location),  
     LEFT(pr2.hierarchy_location, LOCATE('.', pr2.hierarchy_location) - 1),  
     pr2.hierarchy_location))

rn  hl  request                      author  category        l_name  f_name  Beitrag
------------------------------------------------------------------------------------
1   1   Staub                        adam    Erschaffung     Mensch  Adam    1
1   1   Staub                        adam    Erschaffung     Mensch  Adam    1
1   1   Staub                        adam    Erschaffung     Mensch  Adam    1
1   1   Staub                        adam    Erschaffung     Mensch  Adam    1
5   2   Mmh, ein Apfel               eva     Nahrung         Mensch  Eva     2
5   2   Mmh, ein Apfel               eva     Nahrung         Mensch  Eva     2
5   2   Mmh, ein Apfel               eva     Nahrung         Mensch  Eva     2
5   2   Mmh, ein Apfel               eva     Nahrung         Mensch  Eva     2
9   3   Vegetarier leben gefährlich  abel    Risikofaktoren  Mensch  Abel    3

Du siehst, dass man nun wunderbar nach allen Spalten gruppieren kann - und das Auftreten
der request_number zählen kann. Nicht vergessen: am Schluß von der Anzahl 1 abziehen, da
derzeit ja der Startbeitrag auch als Antwort gezählt wird. Die Spalte Beitrag diente im
vorhergehenden Zwischenschritt nur zur Illustration, wir lassen sie jetzt einfach weg.

f) Wir kommen somit zu der Anweisung, die uns das gewünschte Resultat liefert:

SELECT  
  pr1.request_number rn,  
  pr1.hierarchy_location hl,  
  pr1.request,  
  pr1.author,  
  prc.category,  
  pu.last_name l_name,  
  pu.first_name f_name,  
  COUNT(pr1.request_number) - 1 AS CommentCount  
FROM prb_request pr1  
INNER JOIN prb_request_category prc  
ON pr1.request_number = prc.request_number  
INNER JOIN prb_user pu  
ON pr1.author = pu.login_name  
INNER JOIN prb_request pr2  
ON pr1.hierarchy_location =  
  (IF (LOCATE('.', pr2.hierarchy_location),  
     LEFT(pr2.hierarchy_location, LOCATE('.', pr2.hierarchy_location) - 1),  
     pr2.hierarchy_location))  
GROUP BY  
  pr1.request_number,  
  pr1.hierarchy_location,  
  pr1.request,  
  pr1.author,  
  prc.category,  
  pu.last_name,  
  pu.first_name

rn  hl  request                      author  category        l_name  f_name  CommentCount
-----------------------------------------------------------------------------------------
1   1   Staub                        adam    Erschaffung     Mensch  Adam     3
5   2   Mmh, ein Apfel               eva     Nahrung         Mensch  Eva      3
9   3   Vegetarier leben gefährlich  abel    Risikofaktoren  Mensch  Abel     0

Ich hoffe, Du liest dieses Posting und verstehst anhand der Einzelschritte,
warum die Anweisung das gewünschte Ergebnis zurückliefert.

Freundliche Grüße

Vinzenz