Rolf B: DB: Problem mit SUM() in Subselect

Beitrag lesen

Hallo Nico,

du machst das, was alle MYSQL-Verwender tun: sie ignorieren den SQL Standard.

Die Grundregel ist: Eine Spalte ist aggregiert (also SUM oder COUNT) oder nicht. Wenn ich aggregierte und nicht-aggregierte Spalten mischen will, dann muss ich nach den nicht-aggregierten Spalten gruppieren. Alles andere ist ein logischer Fehler.

Der Bullshit bei MYSQL ist, dass das Fehlen von GROUP BY nicht ankreidet. Statt dessen wählt es für nicht-aggregierte Spalten einfach irgendeinen Wert aus dieser Spalte aus, zumeist den ersten.

Das ruiniert Dir deinen Teil-Select für die Spielzeit:

SELECT
   spieltag.saison_id AS saison_id_,
   SUM(spielerstatistik.spielzeit) AS spielzeit
FROM
   spielerstatistik 
JOIN
   spieltag
        ON(spieltag.id = spielerstatistik.spieltag_id)
WHERE 
   spielerstatistik.spieler_id = 12

Ein ordentlicher SQL Server würde Dir das um die Ohren hauen, weil Du spieltag.saison_id in der SELECT Liste hast, nach dieser Spalte aber nicht gruppierst. MYSQL will „anfängerfreundlich“ und fehlertolerant sein und nimmt einfach die ID von irgendeiner Zeile. Bei Dir ist es die Saison-ID 9.

Das ist nicht anfängerfreundlich. Es ist schlichtweg verwirrender Blödsinn. MYSQL hat keinerlei Anhaltspunkt dafür, aus welcher Zeile es die ID nehmen könnte. Ein korrekt implementierter SQL Server darf dieses Statement nicht ausführen.

Lösung: Verschiebe die GROUP BY Klausel in den Teilselect für die Spielzeiten. Einen Aliasnamen für die Saison-ID brauchst Du da übrigens nicht. Klammern um die ON-Bedingung brauchst Du auch nicht.

Dann bekommst Du aus dem Subselect nicht nur eine Zeile, sondern eine pro Saison und je Saison die Summe der Spielzeiten, und welche Summe es auch sonst noch sein soll. Das Gruppieren auf oberster Ebene ist dann nicht mehr nötig.

Ich habe die Alias-Namen mal verkürzt, damit die Zeilen nicht zu lang werden. Eigentlich müsste man die Abfrage auf die Spieler-ID aus dem Stat-Subselect herausziehen, um die redundante Verwendung der 12 loszuwerden, aber ich weiß nicht, ob MYSQL das dann noch gut optimiert.

SELECT 		
       S_Sp.saison_id AS saison_id,
       saison.name    AS saison_name,
       saison.jahr    AS saison_jahr,
       Stat.spielzeit, Stat.einsätze, Stat.fouls
FROM
       saison_spieler AS S_Sp
JOIN
       saison
       ON saison.id = S_Sp.saison_id
JOIN
      (SELECT   T.saison_id,
                SUM(Sp_Stat.spielzeit) AS spielzeit,
                COUNT(*) AS einsätze,
                SUM(Sp_Stat.fouls) AS fouls
       FROM     spielerstatistik Sp_Stat
       JOIN     spieltag T ON T.id = Sp_Stat.spieltag_id
       WHERE    Sp_Stat.spieler_id = 12
       GROUP BY T.saison_id
      ) AS Stat    
      ON Stat.saison_id_ = S_Sp.saison_id 
WHERE
      S_Sp.spieler_id = 12

Ich hoffe, ich habe mich nicht vertippt - ich kann's ja nicht testen.

Rolf

--
sumpsi - posui - obstruxi