Bietet Rezepte, Lösungen und ausführliche Beispiele rund um gesundes SQL und zufriedene Datenbanken.
Daten sortieren ist eine besondere Stärke von SQL-Datenbanken. Sortiert wird die Ausgabe mit Hilfe des ORDER BY Befehls. Ohne ORDER BY ist die Ausgabereihenfolge (per Definition) nicht garantiert, auch wenn einige Datenbanken unter bestimmten Voraussetzungen (z. B. beim GROUP BY) bereits sortieren. Grundregel sollte immer sein, ohne ORDER BY ist die Reihenfolge der Ausgabe zufällig und nicht garantiert.
[SQL-Abfrage]
ORDER BY [sortiertliste]
Die Reihenfolge der Sortierung kann mit ASC (ascending) aufsteigend oder DESC (descending) absteigend beeinflußt werden.
DESC - absteigende Sortierung, der größte Wert steht oben.
ASC - (Default) aufsteigende Sortierung, der kleinste Wert steht oben
Wird weder DESC noch ASC als Sortierreihenfolge angegeben, kommt per Default ASC zum Zuge. ORDER BY steht (bis auf wenige Ausnahmen wie LIMIT, FOR UPDATE, .. ) als letzte Zeile in der SQL-Abfrage.
* IBM DB2 9.1
* MySQL 5.1.30
* ORACLE 10g 10.2 EE
Bei statistischen Auswertungen wird häufig nach den verschiedensten Kriterien sortiert. Den Zahlenjongleuren fallen immer spannende Fragen ein. Deshalb stammt unser Beispiel auch aus dem Controlling.
/**
* Controlling
*/
CREATE TABLE controlling (
id INT NOT NULL,
kunde_id INT NOT NULL,
plz INT NOT NULL,
umsatz DEC(10,2) NOT NULL,
letzt_umsatz DATE NOT NULL,
status CHAR(1) NOT NULL,
PRIMARY KEY (id)
);
/**
* IBM DB2, MySQL Testdaten
*/
INSERT INTO controlling VALUES
( 1, 1, 20000, 200, '2010-09-01', 'A' ),
( 2, 2, 20500, 100, '2010-09-01', 'C' ),
( 3, 3, 21000, 150, '2010-09-02', 'B' ),
( 4, 1, 20000, 90, '2010-09-05', 'C' ),
( 5, 2, 20500, 130, '2010-09-07', 'A' ),
( 6, 3, 21000, 180, '2010-09-09', 'B' ),
( 7, 1, 20000, 90, '2010-09-09', 'C' ),
( 8, 1, 20500, 30, '2010-09-10', 'A' ),
( 9, 3, 21000, 180, '2010-09-10', 'B' );
/**
* ORACLE Testdaten
*/
INSERT INTO controlling VALUES ( 1, 1, 20000, 200, TO_DATE('2010-09-01','YYYY-MM-DD'), 'A' );
INSERT INTO controlling VALUES ( 2, 2, 20500, 100, TO_DATE('2010-09-01','YYYY-MM-DD'), 'C' );
INSERT INTO controlling VALUES ( 3, 3, 21000, 150, TO_DATE('2010-09-02','YYYY-MM-DD'), 'B' );
INSERT INTO controlling VALUES ( 4, 1, 20000, 90, TO_DATE('2010-09-05','YYYY-MM-DD'), 'C' );
INSERT INTO controlling VALUES ( 5, 2, 20500, 130, TO_DATE('2010-09-07','YYYY-MM-DD'), 'A' );
INSERT INTO controlling VALUES ( 6, 3, 21000, 180, TO_DATE('2010-09-09','YYYY-MM-DD'), 'B' );
INSERT INTO controlling VALUES ( 7, 1, 20000, 90, TO_DATE('2010-09-09','YYYY-MM-DD'), 'C' );
INSERT INTO controlling VALUES ( 8, 1, 20500, 30, TO_DATE('2010-09-10','YYYY-MM-DD'), 'A' );
INSERT INTO controlling VALUES ( 9, 3, 21000, 180, TO_DATE('2010-09-10','YYYY-MM-DD'), 'B' );
Umsatzliste im PLZ-Bereich "21" für alle Kunden.
SELECT kunde_id, plz, umsatz
FROM controlling
WHERE plz BETWEEN 21000 AND 21999
ORDER BY umsatz DESC;
+----------+-------+--------+
| kunde_id | plz | umsatz |
+----------+-------+--------+
| 3 | 21000 | 180.00 |
| 3 | 21000 | 180.00 |
| 3 | 21000 | 150.00 |
+----------+-------+--------+
3 rows in set (0.02 sec)
Kumulierte Umsatzliste pro Kunde.
SELECT kunde_id, SUM(umsatz)
FROM controlling
GROUP BY kunde_id
ORDER BY umsatz DESC;
+----------+-------------+
| kunde_id | SUM(umsatz) |
+----------+-------------+
| 1 | 410.00 |
| 3 | 510.00 |
| 2 | 230.00 |
+----------+-------------+
3 rows in set (0.00 sec)
Während DB2 und ORACLE bei Listing 6: eine Fehlermeldung bringen, liefert MySQL eine Ausgabe. Leider ist das Ergebnis falsch! Die Ausgabe ist nicht korrekt nach der berechneten Summe des Umsatzes pro Kunde sortiert.
Korrekt kann die Abfrage auf folgende zwei Arten ausgeführt werden. Listing 7: sortiert nach der laufenden Spaltennummer in der SELECT-Liste. SUM(umsatz) ist die zweite Spalte, als ORDER BY 2.
SELECT kunde_id, SUM(umsatz)
FROM controlling
GROUP BY kunde_id
ORDER BY 2 DESC;
+----------+-------------+
| kunde_id | SUM(umsatz) |
+----------+-------------+
| 3 | 510.00 |
| 1 | 410.00 |
| 2 | 230.00 |
+----------+-------------+
3 rows in set (0.00 sec)
Oder alternativ kann der Aggregatspalte auch ein Alias-Namen gegeben werden, der dann auch wieder zur Sortierung verwendet werden kann.
SELECT kunde_id, SUM(umsatz) AS sum_umsatz
FROM controlling
GROUP BY kunde_id
ORDER BY sum_umsatz DESC;
+----------+------------+
| kunde_id | sum_umsatz |
+----------+------------+
| 3 | 510.00 |
| 1 | 410.00 |
| 2 | 230.00 |
+----------+------------+
3 rows in set (0.00 sec)
Umsatzliste nach Status und Umsatz. Wobei der Status in der Reihenfolge "B", "A", "C" und aufsteigendem Umsatz ausgegeben werden soll. Mit CASE wird temporär eine eigenständige Sortierreihenfolge erzeugt. Jedem Status wird ein Zahlenwert zugewiesen und damit die gewünschte Sortierung ermöglicht.
SELECT kunde_id, status, umsatz
FROM controlling
ORDER BY CASE status
WHEN 'B' THEN 3
WHEN 'A' THEN 2
WHEN 'C' THEN 1
END DESC,
umsatz;
+----------+--------+--------+
| kunde_id | status | umsatz |
+----------+--------+--------+
| 3 | B | 150.00 |
| 3 | B | 180.00 |
| 3 | B | 180.00 |
| 1 | A | 30.00 |
| 2 | A | 130.00 |
| 1 | A | 200.00 |
| 1 | C | 90.00 |
| 1 | C | 90.00 |
| 2 | C | 100.00 |
+----------+--------+--------+
9 rows in set (0.00 sec)
Täglichen Status- und Aktionscode auf den Umsatz anwenden und Rabatte berücksichtigen. Als Zeitraum gilt der letzte Umsatz ab 01.09.2010 bis heute.
/**
* DB2, MySQL
*/
SELECT letzt_umsatz, AVG(CASE status
WHEN 'B' THEN 0.3 * umsatz
WHEN 'A' THEN 0.15 * umsatz
WHEN 'C' THEN 0.10 * umsatz
END) AS avg_code_umsatz
FROM controlling
WHERE letzt_umsatz BETWEEN '2010-09-01' AND CURRENT_DATE
GROUP BY letzt_umsatz
ORDER BY 2;
/**
* ORACLE
*/
SELECT letzt_umsatz, AVG(CASE status
WHEN 'B' THEN 0.3 * umsatz
WHEN 'A' THEN 0.15 * umsatz
WHEN 'C' THEN 0.10 * umsatz
END) AS avg_code_umsatz
FROM controlling
WHERE letzt_umsatz BETWEEN TO_DATE('2010-09-01','YYYY-MM-DD') AND CURRENT_DATE
GROUP BY letzt_umsatz
ORDER BY 2;
+--------------+-----------------+
| letzt_umsatz | avg_code_umsatz |
+--------------+-----------------+
| 2010-09-05 | 9.00000000 |
| 2010-09-07 | 19.50000000 |
| 2010-09-01 | 20.00000000 |
| 2010-09-10 | 29.25000000 |
| 2010-09-09 | 31.50000000 |
| 2010-09-02 | 45.00000000 |
+--------------+-----------------+
6 rows in set (0.00 sec)
Mit ORDER BY kann die Ausgabe der SQL-Abfragen sehr flexibel gesteuert werden. Speziell in der Kombination mit dem CASE Befehl können spezielle Anforderungen abgedeckt werden. Unschön ist, dass MySQL beim obigen Beispiel (Listing 6:) leider falsche Ergebnisse erzeugt. Wie immer ist die unterschiedliche Auslegung des SQL-Standards, der drei gezeigten Datenbanken, auch diesmal wieder ein Thema.