Wenn du mit SQL arbeitest, stößt du schnell auf Situationen, in denen du Daten nicht nur abrufen, sondern auch sinnvoll „in eine Reihenfolge bringen“ musst. Genau hier kommen Rangfunktionen ins Spiel. Sie helfen dir dabei, Datensätze zu bewerten, zu sortieren und nachvollziehbare Platzierungen zu erstellen – zum Beispiel für Bestenlisten, Umsatzrankings oder Top-N-Auswertungen.
Typische Anwendungsfälle sind etwa:
- Die Top-Verkäufer eines Monats ermitteln
- Schüler oder Produkte nach Punkten oder Umsatz bewerten
- Daten innerhalb von Gruppen vergleichen (z. B. pro Kategorie oder Region)
In SQL gibt es dafür drei zentrale Fensterfunktionen:
ROW_NUMBER()RANK()DENSE_RANK()
Diese drei Funktionen sehen auf den ersten Blick ähnlich aus, verhalten sich aber unterschiedlich, sobald es gleiche Werte (Gleichstände) gibt. Genau diese Unterschiede entscheiden oft darüber, ob deine Auswertung korrekt oder verfälscht ist.
In diesem Artikel lernst du Schritt für Schritt:
- wie diese drei Funktionen funktionieren
- wann du welche Funktion einsetzen solltest
- und wie du typische Fehler vermeidest
Am Ende kannst du Rangfolgen in SQL sicher erstellen und weißt genau, welche Funktion in welcher Situation die richtige ist.
Wichtig: Dieses Wissen ist nicht nur für klassische Ranking-Probleme relevant, sondern auch für viele praktische SQL-Szenarien – unabhängig davon, mit welchen SQL Datenbanktypen du arbeitest. Ob relationale Datenbanken wie MySQL, PostgreSQL oder SQL Server: Fensterfunktionen gehören überall zu den wichtigsten Werkzeugen für moderne Datenanalysen.
Grundlagen: Syntax & Funktionsweise
Bevor wir uns die einzelnen Funktionen im Detail anschauen, ist es wichtig zu verstehen, wie Fensterfunktionen in SQL grundsätzlich funktionieren. Denn ROW_NUMBER(), RANK() und DENSE_RANK() basieren alle auf demselben Prinzip: der OVER()-Klausel.
Die OVER()-Klausel
Die OVER()-Klausel definiert den „Fensterbereich“, in dem eine Funktion arbeitet. Anders als klassische Aggregatfunktionen wie SUM() oder COUNT() reduziert sie nicht die Anzahl der Zeilen, sondern berechnet Werte pro Zeile.
Dabei spielen zwei wichtige Bestandteile eine Rolle:
PARTITION BY: Teilt die Daten in Gruppen (z. B. nach Kategorie, Region oder Produkt)ORDER BY: Bestimmt die Reihenfolge innerhalb dieser Gruppen
Ein einfaches Beispiel:
SELECT
produkt,
kategorie,
umsatz,
ROW_NUMBER() OVER (PARTITION BY kategorie ORDER BY umsatz DESC) AS rang
FROM verkauf;
Hier wird für jede Kategorie eine eigene Rangfolge erstellt – sortiert nach Umsatz.
Gemeinsamkeiten der drei Funktionen
Alle drei Funktionen (ROW_NUMBER(), RANK(), DENSE_RANK()) teilen sich dieselbe Grundstruktur:
FUNKTION() OVER (PARTITION BY ... ORDER BY ...)
Unabhängig von der verwendeten SQL-Datenbank – egal ob du mit MySQL, PostgreSQL, SQL Server oder anderen SQL Datenbanktypen arbeitest – bleibt dieses Grundprinzip gleich. Das macht Fensterfunktionen zu einem sehr mächtigen und portablen Werkzeug in der Datenanalyse.
Der entscheidende Unterschied zwischen den drei Funktionen liegt später ausschließlich in der Art, wie gleiche Werte (Ties) behandelt werden.
ROW_NUMBER() – Der eindeutige Zähler
Die Funktion ROW_NUMBER() vergibt für jede Zeile eine eindeutige, fortlaufende Nummer innerhalb eines definierten Fensters. Dabei spielt es keine Rolle, ob Werte gleich sind – jede Zeile bekommt immer einen eigenen Rang.
Definition
ROW_NUMBER() erzeugt eine eindeutige Nummerierung ohne Duplikate und ohne Berücksichtigung von Gleichständen.
Beispiel
Angenommen, du hast eine Tabelle mit Verkäufen und möchtest jede Zeile eindeutig nummerieren:
SELECT
verkaufs_id,
produkt,
umsatz,
ROW_NUMBER() OVER (ORDER BY umsatz DESC) AS rn
FROM verkauf;
Ergebnis: Selbst wenn zwei Produkte denselben Umsatz haben, bekommen sie unterschiedliche Nummern.
Typische Anwendung
- Paginierung (z. B. Seite 1, 2, 3 in Webanwendungen)
- Auswahl der ersten Zeile pro Gruppe
- Eindeutige Nummerierung von Datensätzen
Ein klassisches Beispiel ist die Auswahl der besten Zeile pro Kategorie:
SELECT *
FROM (
SELECT
produkt,
kategorie,
umsatz,
ROW_NUMBER() OVER (PARTITION BY kategorie ORDER BY umsatz DESC) AS rn
FROM verkauf
) t
WHERE rn = 1;
Hier bekommst du für jede Kategorie genau den besten Datensatz zurück.
Im Vergleich zu anderen SQL-Funktionen und unabhängig von den eingesetzten SQL Datenbanktypen ist ROW_NUMBER() besonders streng: Es ignoriert Gleichstände komplett und sorgt immer für eine klare, eindeutige Reihenfolge.
RANK() – Der Lücken-Rang
Die Funktion RANK() wird verwendet, wenn gleiche Werte auch denselben Rang bekommen sollen. Im Gegensatz zu ROW_NUMBER() berücksichtigt RANK() also Gleichstände. Allerdings entstehen dabei sogenannte Ranglücken.
Definition
RANK() vergibt denselben Rang für identische Werte und überspringt anschließend die entsprechenden Rangpositionen.
Beispiel
Stell dir eine Tabelle mit Verkäufen vor:
SELECT
produkt,
umsatz,
RANK() OVER (ORDER BY umsatz DESC) AS rang
FROM verkauf;
Angenommen, zwei Produkte haben den höchsten Umsatz, dann erhalten beide Rang 1. Der nächste Rang ist dann nicht 2, sondern 3.
Typisches Ergebnis könnte so aussehen:
Produkt A | 1000 | 1
Produkt B | 1000 | 1
Produkt C | 900 | 3
Produkt D | 800 | 4
Vergleich zu ROW_NUMBER()
ROW_NUMBER(): immer eindeutige Nummern, keine GleichständeRANK(): gleiche Werte = gleicher Rang, aber mit Lücken danach
Typische Anwendung
- Wettbewerbsplatzierungen (z. B. Sportergebnisse)
- Top-N-Auswertungen mit echten Gleichständen
- Ranking-Reports, bei denen Lücken logisch sind
Gerade in Analysen über verschiedene SQL Datenbanktypen hinweg ist RANK() hilfreich, wenn die „echte Platzierung“ wichtig ist und Gleichstände sichtbar bleiben sollen.
Direkter Vergleich der drei Funktionen an einer Tabelle
Um die Unterschiede zwischen ROW_NUMBER(), RANK() und DENSE_RANK() wirklich zu verstehen, schauen wir uns alle drei Funktionen im direkten Vergleich an. Dafür nutzen wir eine einfache Beispieltabelle mit Verkäufen.
Beispieldaten
produkt umsatz
-------------------
A 1000
B 1000
C 900
D 800
SQL-Abfrage mit allen drei Funktionen
SELECT
produkt,
umsatz,
ROW_NUMBER() OVER (ORDER BY umsatz DESC) AS row_num,
RANK() OVER (ORDER BY umsatz DESC) AS rank_num,
DENSE_RANK() OVER (ORDER BY umsatz DESC) AS dense_rank_num
FROM verkauf;
Ergebnisvergleich
produkt | umsatz | row_num | rank_num | dense_rank_num
------------------------------------------------------
A | 1000 | 1 | 1 | 1
B | 1000 | 2 | 1 | 1
C | 900 | 3 | 3 | 2
D | 800 | 4 | 4 | 3
Visueller Vergleich
- ROW_NUMBER(): immer eindeutig, keine Rücksicht auf gleiche Werte
- RANK(): gleiche Werte teilen sich Rang, danach entstehen Lücken
- DENSE_RANK(): gleiche Werte teilen sich Rang, ohne Lücken
Dieser direkte Vergleich ist besonders wichtig, wenn du Auswertungen über verschiedene SQL Datenbanktypen hinweg erstellst, da alle drei Funktionen zwar überall verfügbar sind, aber oft falsch interpretiert werden.
Praxisbeispiele (mit Code)
Nachdem du die Unterschiede zwischen ROW_NUMBER(), RANK() und DENSE_RANK() kennst, schauen wir uns jetzt typische Praxisfälle an. Genau hier zeigt sich, warum diese Funktionen im Alltag mit SQL so wichtig sind – unabhängig davon, mit welchen SQL Datenbanktypen du arbeitest.
1. Top-3 pro Kategorie mit ROW_NUMBER()
Wenn du pro Kategorie nur die besten drei Einträge möchtest, eignet sich ROW_NUMBER() besonders gut, weil du damit eindeutig begrenzen kannst:
SELECT *
FROM (
SELECT
produkt,
kategorie,
umsatz,
ROW_NUMBER() OVER (
PARTITION BY kategorie
ORDER BY umsatz DESC
) AS rn
FROM verkauf
) t
WHERE rn <= 3;
Hier bekommst du pro Kategorie exakt drei Zeilen – ohne Gleichstände zu berücksichtigen.
2. Rangliste mit gleichen Plätzen (RANK() vs. DENSE_RANK())
Wenn du echte Platzierungen darstellen willst, kannst du beide Funktionen vergleichen:
SELECT
produkt,
umsatz,
RANK() OVER (ORDER BY umsatz DESC) AS rank_pos,
DENSE_RANK() OVER (ORDER BY umsatz DESC) AS dense_rank_pos
FROM verkauf;
Wann ist das wichtig?
RANK(): wenn Gleichstände „Lücken“ in der Platzierung verursachen dürfen (z. B. Sport)DENSE_RANK(): wenn du eine kompakte Rangliste ohne Sprünge brauchst (z. B. interne Reports)
3. Beste 5 Einträge – wann welche Funktion?
Für Top-N-Abfragen ist die Wahl der Funktion entscheidend:
ROW_NUMBER(): wenn du exakt N Zeilen brauchstRANK(): wenn Gleichstände mehrere Plätze „teilen“ dürfenDENSE_RANK(): wenn du Gleichstände zusammenfassen willst, ohne Lücken
Beispiel mit DENSE_RANK():
SELECT *
FROM (
SELECT
produkt,
umsatz,
DENSE_RANK() OVER (ORDER BY umsatz DESC) AS dr
FROM verkauf
) t
WHERE dr <= 5;
So kannst du sicherstellen, dass alle relevanten Einträge berücksichtigt werden, auch bei gleichen Werten.
Wichtige Fallstricke & Best Practices
Auch wenn ROW_NUMBER(), RANK() und DENSE_RANK() relativ einfach wirken, gibt es in der Praxis einige typische Fehlerquellen. Wenn du sie kennst, sparst du dir später viel Debugging – besonders in komplexeren Projekten mit unterschiedlichen SQL Datenbanktypen.
1. ORDER BY in OVER() ist zwingend
Ein häufiger Fehler ist das Vergessen von ORDER BY innerhalb der OVER()-Klausel. Ohne diese Sortierung ist das Ergebnis entweder fehlerhaft oder nicht deterministisch.
-- korrekt
ROW_NUMBER() OVER (ORDER BY umsatz DESC)
-- falsch (führt zu undefiniertem Verhalten)
ROW_NUMBER() OVER ()
Ohne klare Sortierung gibt es keine sinnvolle Rangfolge.
2. PARTITION BY bewusst einsetzen
Mit PARTITION BY teilst du deine Daten in Gruppen. Wenn du das vergisst, wird die Rangfolge über die gesamte Tabelle berechnet.
ROW_NUMBER() OVER (PARTITION BY kategorie ORDER BY umsatz DESC)
Das ist besonders wichtig bei Top-N-Analysen pro Gruppe.
3. Performance und Indexierung
Fensterfunktionen können bei großen Datenmengen teuer werden. Ein wichtiger Best Practice ist daher die Indexierung der Spalten, die in ORDER BY verwendet werden.
- Index auf Umsatz- oder Datumsfelder kann Sortierung beschleunigen
- Besonders relevant bei Millionen von Datensätzen
4. Verhalten bei NULL-Werten
NULL-Werte können je nach Datenbank unterschiedlich behandelt werden. Sie können entweder als kleinste oder größte Werte sortiert werden.
ORDER BY umsatz DESC
Je nach SQL-System (z. B. PostgreSQL vs. MySQL vs. SQL Server) kann sich die Platzierung von NULL unterscheiden. Das ist wichtig, wenn du mit verschiedenen SQL Datenbanktypen arbeitest.
5. Best Practices im Überblick
- Immer explizit
ORDER BYin Fensterfunktionen verwenden PARTITION BYnur einsetzen, wenn wirklich Gruppenbildung nötig ist- Bei großen Datenmengen Indexe prüfen
- NULL-Sortierung datenbankspezifisch testen
Wenn du diese Punkte beachtest, vermeidest du die häufigsten Fehler bei Ranking-Abfragen und bekommst stabile, reproduzierbare Ergebnisse.
Zusammenfassung & Cheatsheet
Zum Abschluss fassen wir die wichtigsten Unterschiede der drei Fensterfunktionen noch einmal kompakt zusammen. Wenn du diese Merkmale verinnerlichst, kannst du in Zukunft schnell entscheiden, welche Funktion du in deiner SQL-Abfrage brauchst.
Merkregeln
ROW_NUMBER()→ immer eindeutige Nummern ohne GleichständeRANK()→ gleiche Werte bekommen denselben Rang, danach entstehen LückenDENSE_RANK()→ gleiche Werte bekommen denselben Rang, ohne Lücken
Cheatsheet (tabellarisch)
| Funktion | Gleichstände | Lücken in Rangfolge | Typischer Einsatz |
|---|---|---|---|
ROW_NUMBER() |
Nein | Nein | Paginierung, eindeutige Auswahl |
RANK() |
Ja | Ja | Wettbewerbe, klassische Platzierungen |
DENSE_RANK() |
Ja | Nein | Komprimierte Rankings ohne Sprünge |
Wichtiger Kontext
Diese Funktionen gehören zu den wichtigsten Werkzeugen in der SQL-Analyse und funktionieren unabhängig von den eingesetzten SQL Datenbanktypen. Egal ob du MySQL, PostgreSQL, SQL Server oder andere Systeme nutzt – das Grundprinzip bleibt gleich.
Wenn du diese drei Funktionen sicher beherrschst, kannst du viele komplexe Ranking- und Analyseprobleme in SQL deutlich einfacher lösen.
Weiterführende Links & Übungsaufgaben
Übungsplattformen
- db<>fiddle – ideal, um SQL direkt im Browser zu testen
- SQL Fiddle – klassische Umgebung für SQL-Experimente
Übungsaufgaben
Versuche folgende Aufgaben selbst umzusetzen:- Erstelle eine Rangliste von Produkten nach Umsatz mit
RANK() - Gib pro Kategorie nur den besten Datensatz mit
ROW_NUMBER()aus - Vergleiche
DENSE_RANK()undRANK()anhand eines Datensatzes mit Gleichständen
Weiterführende Fensterfunktionen
Wenn du noch tiefer in SQL einsteigen möchtest, solltest du dir auch folgende Funktionen anschauen:LEAD()– Zugriff auf nächste ZeilenwerteLAG()– Zugriff auf vorherige ZeilenwerteNTILE()– Aufteilung in gleich große Gruppen
Fazit
Rangfunktionen gehören zu den wichtigsten Konzepten in der SQL-Analyse. Sobald du sie sicher einsetzen kannst, wirst du viele Auswertungen deutlich einfacher lösen können – unabhängig davon, mit welchen SQL Datenbanktypen du arbeitest.Korrekte Verwendung von EXISTS und NOT EXISTS
In der Welt der SQL-Analyse sind Fensterfunktionen (Window Functions) ein...
Artikel lesenAudit-Trails implementieren: Wer hat was wann geändert?
Eine kritische Kundennummer wurde in deiner Datenbank geändert, und die...
Artikel lesenVertikale vs. horizontale Skalierung in Datenbanken: Grundlagen, Beispiele & Best Practices
Wenn eine Datenbank langsam wird oder unter Last zusammenbricht, merkt...
Artikel lesen5 SQL-Funktionen, die kaum jemand richtig kennt
SQL ist aus der Arbeit mit Datenbanken nicht wegzudenken. Jeder,...
Artikel lesen