Bietet Rezepte, Lösungen und ausführliche Beispiele rund um gesundes SQL und zufriedene Datenbanken.
Top-Ten Listen und Statistische Auswertungen werden häufig benötigt. Zum Einen werden dafür Gruppierungen benötigt, zum anderen müssen "vertikale" Ergebnisse nebeneinander, also "horizontal" dargestellt werden. Diese Anforderung widerspricht eigentlich den üblichen Prinzipien von SQL, aber trotzdem will der Chef, der Controller und der VB (Vertriebsbeauftragte) seine gewohnte Auswertung. Also an's Werk...
* MySQL 5.1.30
Die Datenbank-Tabellen hierfür sehen wie folgt aus:
/**
* Kunden
*/
CREATE TABLE kunde (
id INT NOT NULL,
name VARCHAR(30) NOT NULL,
plz INT NOT NULL,
PRIMARY KEY (id)
);
/**
* Rechnungen
*/
CREATE TABLE rechnung (
id INT NOT NULL,
kunde_id INT NOT NULL,
datum DATE NOT NULL,
betrag DEC(10,2) NOT NULL,
PRIMARY KEY (id),
CONSTRAINT fk_rechnung_01
FOREIGN KEY (kunde_id)
REFERENCES kunde (id)
);
Nun noch die Testdaten für die nachfolgenden SQL-Abfragen.
/**
* Kunden
*/
INSERT INTO kunde VALUES
( 1, 'Kunde-1', 20000),
( 2, 'Kunde-2', 30000),
( 3, 'Kunde-3', 40000);
/**
* Rechnungen
*/
INSERT INTO rechnung VALUES
( 1, 1, '2010-01-01', 100),
( 2, 1, '2010-02-01', 500),
( 3, 2, '2010-03-01', 200),
( 4, 1, '2010-02-01', 100),
( 5, 1, '2010-05-01', 400),
( 6, 1, '2010-02-01', 500),
( 7, 3, '2010-01-01', 200),
( 8, 1, '2010-03-01', 200),
( 9, 2, '2010-02-01', 100),
(10, 3, '2010-06-01', 500),
(11, 1, '2010-04-01', 500),
(12, 3, '2010-07-01', 200),
(13, 1, '2010-06-01', 200),
(14, 2, '2010-04-01', 100),
(15, 3, '2010-03-01', 500);
Der Chef möchte seine Top Kunden. Also diese mit dem höchsten Umsatz im laufenden Jahr. Der Umsatz pro Kunde wird errechnet mit SUM(r.betrag). Die Spalte erhält nun den Namen "umsatz". Die Sortierung ist absteigend (DESC), folglich steht der Kunde mit dem höchsten Umsatz oben.
/**
* TOP Kunden
*/
SELECT r.kunde_id, MAX(k.name) AS name, SUM(r.betrag) AS umsatz
FROM rechnung r
JOIN kunde k
ON k.id = r.kunde_id
WHERE r.datum BETWEEN '2010-01-01' AND '2010-12-31'
GROUP BY r.kunde_id
ORDER BY SUM(r.betrag) DESC;
+----------+---------+---------+
| kunde_id | name | umsatz |
+----------+---------+---------+
| 1 | Kunde-1 | 2500.00 |
| 3 | Kunde-3 | 1400.00 |
| 2 | Kunde-2 | 400.00 |
+----------+---------+---------+
3 rows in set (0.00 sec)
Als kleine Variante zur obigen Abfrage, erfolgt hier die Sortierung direkt mit Hilfe der Spalte "umsatz" und nicht mehr nach der Summenberechnung "SUM(r.betrag)".
/**
* TOP Kunden
*/
SELECT r.kunde_id, MAX(k.name) AS name, SUM(r.betrag) AS umsatz
FROM rechnung r
JOIN kunde k
ON k.id = r.kunde_id
WHERE r.datum BETWEEN '2010-01-01' AND '2010-12-31'
GROUP BY r.kunde_id
ORDER BY umsatz DESC;
+----------+---------+---------+
| kunde_id | name | umsatz |
+----------+---------+---------+
| 1 | Kunde-1 | 2500.00 |
| 3 | Kunde-3 | 1400.00 |
| 2 | Kunde-2 | 400.00 |
+----------+---------+---------+
3 rows in set (0.00 sec)
Der Controller möchte seine Quartalsauswertung, soll er bekommen. Für uns natürlich kein Problem. Mit Hilfe des CASE Befehls erzeugen wir vier Spalten mit der Quartalszuordnung und summieren die Beträge.
/**
* Quartalauswertung des Umsatzes
*/
SELECT SUM( CASE
WHEN r.datum BETWEEN '2010-01-01' AND '2010-03-31' THEN r.betrag
END
) AS '1.Quartal',
SUM( CASE
WHEN r.datum BETWEEN '2010-04-01' AND '2010-06-30' THEN r.betrag
END
) AS '2.Quartal',
SUM( CASE
WHEN r.datum BETWEEN '2010-07-01' AND '2010-09-30' THEN r.betrag
END
) AS '3.Quartal',
SUM( CASE
WHEN r.datum BETWEEN '2010-10-01' AND '2010-12-31' THEN r.betrag
END
) AS '4.Quartal'
FROM rechnung r
WHERE r.datum BETWEEN '2010-01-01' AND '2010-12-31';
+-----------+-----------+-----------+-----------+
| 1.Quartal | 2.Quartal | 3.Quartal | 4.Quartal |
+-----------+-----------+-----------+-----------+
| 2400.00 | 1700.00 | 200.00 | NULL |
+-----------+-----------+-----------+-----------+
1 row in set (0.00 sec)
Der VB benötigt eine Darstellung der umsatzstärksten Regionen nach Postleitzahl (plz) sortiert. Da wir nun richtig in Fahrt sind, ist auch dies keine echte Herausforderung mehr.
/**
* Quartalauswertung der umsatzstärksten Regionen
*/
SELECT k.plz,
SUM(r.betrag) AS umsatz,
SUM( CASE
WHEN r.datum BETWEEN '2010-01-01' AND '2010-03-31' THEN r.betrag
END
) AS '1.Quartal',
SUM( CASE
WHEN r.datum BETWEEN '2010-04-01' AND '2010-06-30' THEN r.betrag
END
) AS '2.Quartal',
SUM( CASE
WHEN r.datum BETWEEN '2010-07-01' AND '2010-09-30' THEN r.betrag
END
) AS '3.Quartal',
SUM( CASE
WHEN r.datum BETWEEN '2010-10-01' AND '2010-12-31' THEN r.betrag
END
) AS '4.Quartal'
FROM rechnung r
JOIN kunde k
ON k.id = r.kunde_id
WHERE r.datum BETWEEN '2010-01-01' AND '2010-12-31'
GROUP BY k.plz
ORDER BY umsatz;
+-------+---------+-----------+-----------+-----------+-----------+
| plz | umsatz | 1.Quartal | 2.Quartal | 3.Quartal | 4.Quartal |
+-------+---------+-----------+-----------+-----------+-----------+
| 30000 | 400.00 | 300.00 | 100.00 | NULL | NULL |
| 40000 | 1400.00 | 700.00 | 500.00 | 200.00 | NULL |
| 20000 | 2500.00 | 1400.00 | 1100.00 | NULL | NULL |
+-------+---------+-----------+-----------+-----------+-----------+
3 rows in set (0.00 sec)
Mit Hilfe von GROUP BY, SUM und CASE lassen sich mit wenig Aufwand einfache, Pivot-ähnliche Auswertungen erzeugen. Damit können echte OLAP Funktionen nicht ersetzt werden, aber auf die schnelle so eine Liste zu erstellen, sollte nun kein Problem mehr sein.