Rolf B: SQL-Select-Statement

Beitrag lesen

Hallo Matthias,

genau, und das ursprüngliche Design verletzt die 1. Normalform: Relationen müssen frei von Wiederholungsgruppen sein. Die 5 Spalten mit den Wochentagen sind eine Wiederholungsgruppe - und die Strafe folgt sofort: Redundanz im SQL Statement bei der Summierung.

Beim Normalisieren muss man aber aufpassen: die plan_id ist keine Wiederholungsgruppe. Man muss die Tabelle 1 daher in 2 Tabellen teilen:

Tabelle 1a: id, user_id, plan_id
Tabelle 1b: id, wochentag, option

Darf ich übrigens die Frage stellen, warum es eine id und einer user_id gibt? Kann es zu einer user_id mehrere Zeilen in Tabelle 1 geben? Kann sich in einem existierenden Satz der Tabelle 1 die User-ID nochmal ändern? Wenn beides NEIN ist, könnte man die User-ID vermutlich zum Primary Key erheben und die ID-Spalte weglassen.

Eine separate Tabelle mit Wochentagen macht nur Sinn, wenn den Wochentagen noch weitere Attribute zugeordnet sind. Als reine Wertetabelle ist sie weniger hilfreich - selbst Papst Gregor hat damals die Wochentage nicht verändert. Für eine Übersetzung ist es wurscht, ob ich "monday" auf "Lundi" mappe oder die Wochentag-ID 1.

Das Ausgeben der Zieltabelle ist dann das Bilden einer Pivot-Tabelle, und das ist mit SQL ursprünglich nicht vorgesehen. Sofern der DB-Server keine Pivot-Funktion hat, muss man das von Hand machen, und das geht auch ohne Subselects. Allerdings nicht dynamisch; man muss schon alle Spalten kennen die man braucht. Selbst DB-Server wie MS-SQL, der Sprachelemente zur Pivot-Bildung hat, benötigen diese Information noch. Pivot-Bildung mit dynamischer Spaltenanzahl kenne ich nur aus Excel.

Das Standardpattern für Pivot-Bildung in SQL verwendet CASE Statements. Wenn man Tabelle 1 in table_1a und table_1b aufgeteilt hat, kann man so abfragen:

select option
     , sum(case table_1b.wochentag = 'monday' then 1 end) as monday
     , sum(case table_1b.wochentag = 'tuesday' then 1 end) as tuesday
     , sum(case table_1b.wochentag = 'wednesday' then 1 end) as wednesday
     , sum(case table_1b.wochentag = 'thursday' then 1 end) as thursday
     , sum(case table_1b.wochentag = 'friday' then 1 end) as friday
from options join table_1b ON options = table1b.option 
order by options

Wirklich schöner ist das auch nicht, aber es ersetzt 5 Subselects durch einen Join und könnte daher performanter sein. Muss man messen - und bei kleinen Datenmengen wird es sich nicht auswirken.

Die Alternative besteht darin, die Pivot-Bildung nicht im SQL durchzuführen, sondern einfach nur, wie schon von dedlfix gezeigt, einen GROUP BY zu machen und die Verteilung auf die Wochentage in der Software auszuführen, die das SQL Ergebnis verarbeitet. Eine solche Software könnte auch die Wochentag-Tabelle verwenden, um den SQL-Teil mit den SUM-Zeilen automatisch zu generieren.

Rolf

--
sumpsi - posui - clusi