dedlfix: mySQL - globale Variable?!

Beitrag lesen

echo $begrüßung;

Jetzt nochmal ganz ausführlich, was ich möchte :-)

  1. Personen auslesen, wobei ich anch namen suche. Außerdem möchte ich in diesem Fall nur aktiviert Personen.

Die beiden Bedingungen sind trivial, weswegen ich nicht weiter auf sie eingehe.

  1. Nun möchte ich die Bewertungen dazunehmen.

Wie sollen diese in die Ergebnismenge des ersten Punktes eingebunden werden? Das unterschlägst du jedes Mal.

Die Bewertung wollte ich folgendermaßen ausrechnen lassen:
SELECT SUM(b_aussehen)/COUNT(b_aussehen) AS AussehenBewertung, SUM(b_charakter)/COUNT(b_charakter) AS CharakterBewertung, SUM(b_sonstiges)/COUNT(b_sonstiges) AS SonstigeBewertung FROM bewertung WHERE person='1' ORDER BY id DESC LIMIT 2

Du hättest schon längst mal die konkrete Ergebnismenge benennen sollen. Diese Berechnung hast du zwar schon im Ausgangsposting angedeutet, aber es fehlten da die konkreten Beziehungen zu den beteiligten Tabellen. Aus obiger Abfrage schließe ich nun, dass das Ergebnis so aussehen soll:

1, Peter Schmidt', bewertung
2, Peter Mustermann', bewertung
3, Peter Nachname', bewertung

Es wäre nämlich auch möglich gewesen, dass du so etwas haben möchtest:

1, Peter Schmidt', bewertung_1
1, Peter Schmidt', bewertung_2
...
1, Peter Schmidt', bewertung_10
2, Peter Mustermann', bewertung_1
2, Peter Mustermann', bewertung_2
...
2, Peter Mustermann', bewertung_10
3, Peter Nachname', bewertung_1
3, Peter Nachname', bewertung_2
...
3, Peter Nachname', bewertung_10

Für diesen Fall wäre nämlich die Lösung anders ausgefallen.

Hier merke ich jedoch gerade, dass den COUNT()s und SUM()s scheinbar das LIMIT egal ist. Die beziehen alle Bewertungen, statt nur 2, mit ein.

LIMIT wirkt erst ganz zum Schluss, wenn bereits alles berechnet ist und die Ergebnismenge feststeht, auf ebendiese ein. Wenn du die zu verwendenden Datensätze damit einschränken willst, brauchst du zunächst eine Ergebnismenge aus allen Datensätzen (sortiert natürlich). In deinem Fall brauchst du also pro Nase eine solche limitierte Ergebnismenge. Erst dann willst du damit Berechnungen anstellen.

Es ist wichtig, die Abarbeitungsreihenfolge der Klauseln eines SELECT-Statements zu kennen, damit man nicht beispielsweise das LIMIT zum falschen Zeitpunkt erwartet. Abgesehen von internen Optimierungen (die für das Verständnis der Arbeitsweise eines SELECT-Statements nicht weiter interessieren), ist die Reihenfolge so:

FROM(inkl.JOIN)->WHERE->SELECT/GROUP->HAVING->ORDER->LIMIT

Bis auf das SELECT (sprich: die Ergebnisspalten) ist das die gleiche Reihenfolge, in der die Klauseln notiert werden müssen. GROUP BY hängt quasi mit SELECT zusammen. Wenn eine GROUP BY-Klausel vorhanden ist, hat das massiven Einfluss auf die verwendbaren Ergebnisspalten-Ausdrücke, doch das ist ein anderes Thema. Aus dieser Reihenfolge geht auch hervor, warum beispielsweise im WHERE keine Aliasnamen aus dem SELECT genommen werden können. Sie sind zu dem Zeitpunkt schlicht noch nicht berechnet.

Ich kenne keine Möglichkeit, Datensätze anders als mit LIMIT auf eine definierte Anzahl hin einzuschränken. Somit muss für jede Person diese Abfrage einzeln durchgeführt werden. In der FROM-Klausel geht es nicht, weil diese eine komplette Datenmenge verlangt. Dafür müsstest du jede einzelne deiner limitierten Datenmengen wieder zu einer großen zusammenfügen. Dann wäre es möglich, sie mit den Personen zu JOINen, zu GROUPieren und die Aggregatfunktionen darauf anzuwenden.

Mir fällt da nur als möglicher Platz die SELECT-Klausel ein. Zu dem Zeitpunkt hast du pro Nase einen Datensatz und weißt auch die ID, die du in einem korrelierten Subquery als Bedingung verwenden kannst. Ein Subquery als Ergebnisspaltenausdruck (sprich: in der SELECT-Klausel) darf aber nur einen einzelnen skalaren Wert liefern (keine Zeile, keine Spalte, keine Matrix). Du willst 3 Werte haben, also brauchst du 3 Subquerys. Und wegen der LIMIT-Problematik musst du die Subquerys nochmal "subqueryen", damit du ein Aggragatergebnis bekommst. So sähe das insgesamt nur mit der Aussehen-Subquery aus:

SELECT
    id,   # AS id_test
    name,
    (
      SELECT SUM(b_aussehen)/COUNT(b_aussehen)
      FROM (
        SELECT b_aussehen
        FROM bewertung
        WHERE person = personen.id
        ORDER BY id DESC
        LIMIT 2
      ) AS aussehen
    ) aussehen
  FROM personen

Doch leider spielt hier MySQL nicht mit, da personen.id sich über zwei Verschachtelungen hinweg auf die äußere Query bezieht. Auch eindeutig benannt als id_test oder als userdefinierte Variable will es nicht. Als Workaround fällt mir da nur ein:

SELECT
    id,
    name,
    (
       SELECT SUBSTRING_INDEX(GROUP_CONCAT(b_aussehen ORDER BY id DESC), ',', 2)
       FROM bewertung
       WHERE person = personen.id
    ) aussehen
  FROM personen

Das ergibt als Ergebnismenge:

id name              aussehen
  1  Peter Schmidt     3,1
  2  Peter Mustermann  NULL
  3  Peter Nachname    3

GROUP_CONCAT() erzeugt eine kommaseparierte Liste in String-Form. SUBSTRING_INDEX() bildet das LIMIT nach, indem es alles ab dem (hier) zweiten Komma abschneidet.

Die eigentliche Berechnung muss dann im auswertenden Programm erfolgen. Unter PHP kann man den Wert in der Spalte "aussehen" am Komma explodieren, und auf das entstandene Array array_sum() und count() anwenden.

echo "$verabschiedung $name";