Sortier-Äquivalente Over-Klauseln


BigQueryDb2 (LUW)DuckDBH2MariaDBMySQLOracle DBPostgreSQLSQL ServerSQLite2007200920112013201520172019202120232025✓ 3.25.0 - 3.51.0⚡ 2012 - 2025ab✓ 9.0 - 18✓ 11gR1 - 23.26.1✓ 8.0.11 - 9.6.0✓ 10.4 - 12.2.2a⚡ 10.2 - 10.3a✓ 1.4.200 - 2.4.240a⚡ 1.4.198 - 1.4.199a✓ 1.0.0 - 1.5.0✓ 10.5 - 12.1.3✓ 2.0+
  1. ⚡Äquivalente Over-Klauseln können unterschiedliche Zeilenreihenfolgen hervorbringen
  2. Scheinbar muss die Rahmung auch gleich sein, damit Over-Klausel als sortier-äquivalent gelten

Im Zentrum dieses Artikels steht eine einfache Frage: Liefert diese Abfrage in den beiden Spalten immer dieselbe Nummerierung?

SELECT ROW_NUMBER() OVER (ORDER BY x)
     , ROW_NUMBER() OVER (ORDER BY x)
  FROM ...

Ich habe hier nichts Relevantes versteckt. Die beiden Spalten sind tatsächlich absolut gleich definiert. Ich kann die Abfrage dennoch etwas erweitern, damit das Problem deutlicher wird.

SELECT ROW_NUMBER() OVER (ORDER BY x)
     , ROW_NUMBER() OVER (ORDER BY x)
     , t.*
  FROM (VALUES (1, 10)
             , (1, 20)
       ) t(x, y)

Die Spalte x in der Order By-Klausel hat also immer denselben Wert. Und damit sind wir beim Kern der Frage: Ist die Sortierung gleichrangiger Zeilen untereinander über Over-Klauseln hinweg einheitlich? Die Antwort lautet, erfreulicher Weise: Ja, der SQL-Standard verlangt das. Wie im oberen Chart zu sehen ist, gibt es dennoch ein System, bei dem das nicht sichergestellt ist.0

Der SQL-Standard definiert dafür einen eigenen Begriff: order-equivalent (dt.: sortier-äquivalent)1. Zwei Over-Klauseln2 sind sortier-äquivalent, wenn die Partition By- (falls vorhanden) und die Order By-Klauseln auf dieselben Spalten beziehen und die effektiven Ergänzungen der Order By-Klausel gleich sind ([Asc|Desc], Nulls [First|Last], Collate). Der SQL-Standard verlangt dann, dass die Zeilenreihenfolge von zwei sortier-äquivalenten Over-Klauseln auch unter gleichrangige Zeilen gleich sein muss.3

Wenn du diese Seite magst, magst du vielleicht auch …

… meine Newsletter abonnieren, gratis Sticker bestellen, mein Buch kaufen oder an einer Schulung teilnehmen.

Neben dieser Anforderung des Standards gibt es auch andere Gründe, warum gleichrangige Zeilen bei gleichlautenden Over-Klauseln gleich sortiert werden sollten: Die Wahrung des Prinzips der geringsten Verblüffung sowie die Optimierung der Antwortzeiten. Wenn auch in keiner Weise vom Standard gefordert, sollten Systemhersteller doch danach trachten, die Anzahl der Sortierungen zu minimieren. Schließlich wäre es Zeitverschwendung, bei den oberen Beispielen zweimal zu sortieren. Vermeidet man gleichwertige Sortierungen, können für gleichrangige Zeilen keine anderen Reihenfolgen entstehen.

Da sortier-äquivalenten Over-Klauseln in der Praxis relativ häufig vorkommen, freut es mich, dass der SQL-Standard hier Weitsicht bewiesen hat.4 Weniger erfreulich ist, dass ähnliche Anwendungsfälle gibt, bei denen die Sortier-Äquivalenz nicht gegeben ist, dennoch eine Gleichbehandlung von gleichrangigen Zeilen wünschenswert wäre. Die folgende Abfrage ist ein Beispiel dafür.

SELECT ROW_NUMBER() OVER (ORDER BY x)
     , ROW_NUMBER() OVER (ORDER BY x DESC)
  FROM (VALUES (1, 10)
             , (1, 20)
       ) t(x, y)

Diese Abfrage liefert sowohl eine aufsteigende- als auch eine abfallende Nummerierung. Hier ist nicht sichergestellt, dass gleichrangige Zeilen gegenläufig sortiert werden. Beim konkreten Beispiel könnte das Ergebnis also auch so aussehen:

… OVER (ORDER BY x)… OVER (ORDER BY x DESC)
11
22

Die durchaus erwartbare Anforderung, dass die Nummerierung gegen-gleich und damit die Summe der beiden Spalten für alle Zeilen dieselbe ist, ist also nicht erfüllt. Eines der getesteten Systeme hat in meinen Tests trotzdem immer das wünschenswerte Ergebnis geliefert.

BigQuery 2026-03-09Db2 (LUW) 12.1.3DuckDB 1.5.0H2 2.4.240MariaDB 12.2.2MySQL 9.6.0Oracle DB 23.26.1PostgreSQL 18SQL Server 2025SQLite 3.51.0Over: Äquivalente Umkehr

Offen gesagt glaube ich, dass das löbliche Verhalten dieses Systems weniger darauf zurückgeht, in solchen Fällen keine überraschenden Ergebnisse zu liefern, sonder eher auf eine Performanceoptimierung zur Vermeidung von Sortierungen. Schließlich liegt das Ergebnis schon richtig vor, wenn man es genau rückwärts verarbeitet. In diesem Aspekt liegt die größte Hoffnung, dass sich dieses Verhalten künftig auch in anderen Systemen wiederfindet.

Denksport

Gibt es weitere Fälle, in denen die Gleichbehandlung von gleichrangigen Zeilen über mehrere Over-Klauseln hinweg wünschenswert sein könnte?⁠5

Stellt sich also die Frage, wie man gegen-gleiche Zählung mit Standard-SQL korrekt umsetzt. Die einfachste Lösung ist wohl die Erweiterung der Order By-Klausel, sodass es keine gleichrangigen Zeilen mehr gibt. Kurz gesagt: den Primärschlüssel in die Order By-Klausel aufnehmen.

Denksport

Warum ist ein Unique-Constraint hier nicht ausreichend?6

Wenn das nicht möglich oder gewünscht ist, muss man eine Abfrage verwenden, die nur sortier-äquivalente Over-Klauseln verwendet. Im folgenden Beispiel sind die Over-Klauseln zwar nicht ident, aber dennoch sortier-äquivalent. Schließlich sind für die Sortier-Äquivalenz nur die Partition By- und Order By-Klauseln relevant, nicht aber die Rahmung (Rows Between). Im Beispiel lege ich den Rahmen über die verbleibenden Zeilen und zähle diese dann.

SELECT ROW_NUMBER() OVER (ORDER BY x)
     , COUNT(*)     OVER (ORDER BY x
                          ROWS BETWEEN
                               CURRENT ROW
                           AND UNBOUNDED FOLLOWING
                         )
  FROM 

Diese Lösung versagt natürlich, wenn die SQL-Engine die Sortier-Äquivalenz nicht sicherstellt. Um dieses Risiko auszuschließen, kann man die Mathematik zu bemühen.

SELECT rn, cnt - rn + 1
  FROM (SELECT ROW_NUMBER() OVER (ORDER BY x) AS rn
             , COUNT(*)     OVER (          ) AS cnt
          FROM 
) t

Der Schlüssel zum Erfolg liegt darin, nur eine Funktion zu nutzen, die für gleichrangige Zeilen verschiedene Ergebnisse liefern kann. Im Beispiel ist das Row_Number. Die Funktion Count wird lediglich genutzt, um alle Zeilen zu zählen. In der äußeren Abfrage wird die Differenz ermittelt und eins erhöht, damit das Ergebnis eines rückläufigen Zählers entsteht.

Denksport

Warum wäre es falsch, die Order By-Klausel auch in der Over-Klausel der Count-Funktion anzugeben?7

Letztendlich handelt dieser Artikel nur von einem Beispiel für Probleme, die durch Nicht-Determinismus entstehen. Insofern ist es eine gute Angewohnheit, nicht-deterministische Ausdrücke möglichst zu vermeiden. Wie zum Beispiel durch Aufnahme des Primärschlüssels in die Order By-Klausel, das Vermeiden von Row-Rahmen, … der Standard hat dafür eine Auflistung.8 Ich denke aber auch, dass die Systemhersteller im Sinne der Benutzerfreundlichkeit durchaus nachbessern könnten. Nicht nur im vom Standard geforderten Ausmaß.

Natürlich werden solche Themen auch in meinem Analyse und Aggregation-Training behandelt.

20 Jahre SQL-Evolution kann man nicht an einem Tag nachholen. Abonniere den Newsletter via E-Mail, Bluesky oder RSS, um sukzessive aufzuholen und modern-sql.com am Radar zu behalten.

Über den Autor

Foto von Markus Winand

Markus Winand gibt auf modern-sql.com Einblick in SQL und zeigt, wie es von verschiedenen Systemen unterstützt wird. Zuvor machte er use-the-index-luke.com, was er noch immer wartet. Markus kann als Trainer, Sprecher und Berater auf winand.at engagiert werden.

Sein Buch kaufen

Titelbild von „SQL Performance Explained“: Eichhörnchen läuft durchs Grass

Die Essenz: SQL-Tuning auf 200 Seiten

Jetzt Kaufen
(Taschenbuch und/oder PDF)

Sein Training

Markus verwandelt veraltetes SQL-92-Wissen in solides und zeitgemäßes SQL-Know-how

Erfahren Sie mehr»

Fußnoten

  1. Die Testfälle hinter den Chart sind etwas komplexer als das gezeigte Beispiel. Danke daran: Nur weil du in einem Fall kein nicht-konformes Ergebnis bekommst, heißt das nicht dass man niemals ein nicht-konformes Ergebnis erhält. Ein Fehler im Chart (⚡) bedeutet, dass mindestens ein Testfall ein nicht-konformes Ergebnis geliefert hat. Umgekehrt bedeutet Erfolg im Chart (✓) nur, dass ich kein nicht-konformes Ergebnis provozieren konnte.

  2. ISO/IEC 9075-2:2023 §7.15 SR 17

  3. Zum Verständnis beziehe ich mir hier auf die weithin bekannte Over-Klausel und verzichte auf die Erwähnung der weniger bekannten Window-Klausel.

  4. ISO/IEC 9075-2:2023 §7.15 GR 4

  5. Und das wohl schon von Anfang an. Zumindest in der 2008er Ausgabe des Standards ist die Anforderung schon enthalten.

  6. Z. B. over(order by a, b) und over(partition by a order by b).

  7. Weil ein Unique-Constraint im Gegensatz zu Primärschlüsseln auch Null-bare Spalten umfassen kann. Da die meisten Unique-Constrains mit einer Nulls Distinct Semantik arbeiten, kann es durch mehrere Null-Werte noch immer zu gleichrangigen Zeilen kommen.

    Wichtige Abweichung: SQLite erlaubt Null in Primärschlüsseln.

  8. Lösung: Weil die Order By-Klausel dann die implizierte Rahmung Range Between Current Row and Unbounded Following auslöst.

  9. ISO/IEC 9075-2:2023 §9.16

Neuigkeiten Abonnieren

Markus Winands MailinglistenMarkus Winands RSS-FeedMarkus Winand auf LinkedInMarkus Winand auf XINGMarkus Winand auf MastodonMarkus Winand auf Bluesky
Copyright 2015-2026 Markus Winand. Alle Rechte vorbehalten.
Impressum | Kontakt | KEINE GEWÄHR | Handelsmarken | Datenschutz und DSGVO