Die ANY_VALUE(…) Aggregatfunktion

Neu in SQL:2023 – und eine bessere Funktion aus 1999


BigQueryDb2 (LUW)DerbyH2MariaDBMySQLOracle DBPostgreSQLSQL ServerSQLite201320152017201920212023⊘ 3.5.7 - 3.50.0⊘ 2008R2 - 2022✓ 16 - 17⊘ 8.3 - 15✓ 23.0 - 23.9d✓ 19c - 21ccd⊘ 11gR1 - 18c⚡ 5.7 - 9.3.0abcd⊘ 5.0 - 5.6⊘ 5.1 - 12.0.2✓ 2.2.220 - 2.3.232⊘ 1.4.192 - 2.1.214⊘ 10.15.1.3 - 10.17.1.0⊘ 9.7 - 12.1.2⚡ 2.0a✓ 2.0a⚡ 2.0ac
  1. ⚡Liefert gelegentlich null, selbst wenn nicht-null Werte in der Gruppe sind
  2. Keine Aggregatfunktion
  3. Ohne Filter-Klausel
  4. Nicht als Fensterfunktion (Over-Klausel)

Die Aggregatfunktion Any_value liefert einen willkürlichen nicht-null Wert aus einer Gruppe von Zeilen.

SELECT produkt_id
     , ANY_VALUE(order_id) AS beispiel_order_id
  FROM bestellposten
 GROUP BY produkt_id

Die Abfrage liefert alle jemals bestellten Produkt_id-Werte und eine zugehörige order_id als Beispiel für jedes Produkt.

Als Aggregatfunktion kann Any_value um die Filter- und Over-Klauseln ergänzt werden0, wenn diese vom System generell unterstützt werden.

BigQuery 2025-09-02aaaaH2 2.3.232MySQL 9.3.0bbbbOracle DB 23.9PostgreSQL 17any_value(…)                  any_value(…) filter(…)        any_value(…)           over(…)any_value(…) filter(…) over(…)
  1. ⚡Liefert gelegentlich null, selbst wenn nicht-null Werte in der Gruppe sind
  2. ⚡Liefert gelegentlich null, selbst wenn nicht-null Werte in der Gruppe sind • ⚡Keine Aggregatfunktion

Beachte, dass Any_value nicht-deterministisch ist. Es könnte jedes Mal einen anderen Wert aus der Gruppe nehmen – d. h. das Ergebnis ist nicht reproduzierbar. Das kann bei Window-Funktionen besonders überraschend sein:

SELECT ANY_VALUE(x) OVER()
  FROM …

Die Any_value-Funktion wird hier „über alles“ angewendet. In diesem Fall liefert jede andere Aggregatfunktion auch das selber Ergebnis für jede Zeile – reproduzierbar. Der SQL-Standard fordert dieses Verhalten allerdings nicht für Any_value. Es könnte für jede Zeile einen anderen Wert auswählen, selbst wenn die Over-Klausel immer dieselben Zeilen umfasst. Wohlgemerkt: Bisher hat keiner meiner Tests jemals ein solches Verhalten beobachtet.

Min und Max erfüllen übrigens alle Anforderungen, die der Standard an Any_value stellt. Man kann also statt Any_value auch Min oder Max verwenden und damit ein deterministisches Ergebnis erhalten. Der Hauptunterschied ist, dass Any_value effizienter umgesetzt sein könnte, da es keinen bestimmten Wert suchen muss, sondern den erstbesten nicht-null Wert nehmen kann.

Zum Abschluss möchte ich noch ausführlich auf einen problematischen Anwendungsfall von Any_value eingehen: Wie man damit umgeht, wenn das Feature T301, „Functional dependencies“ nicht umgesetzt ist. Dazu folgendes Beispiel:

CREATE TABLE kunden (
 id   BIGINT PRIMARY KEY,
 name VARCHAR,
 […]
)
CREATE TABLE bestellungen (
 kunde BIGINT REFERENCES kunden(id),
 von   TIMESTAMP,
 […]
)
SELECT kunden.*, MAX(von)
  FROM kunden
  LEFT JOIN bestellungen
         ON kunden.id = kunden.customer
 GROUP BY kunden.id

Die Abfrage listet alle Kunden auf und liefert den Zeitpunkt der jeweils letzten Bestellung. Beachte, dass alle Spalten der Kunden-Tabelle ausgegeben werden – also auch die Spalte Name. Die Group by-Klausel nennt jedoch nur die Spalte Kunden.id. Ist diese Abfrage überhaupt erlaubt?

Wenn du diese Seite magst, magst du vielleicht auch …

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

Ja, ist sie. Sowohl aus rein logischer Sicht alsauch aus der Sicht des SQL-Standards. Der Knackpunkt ist, dass der Kundenname eine funktionale Abhängigkeit vom Primärschlüssel der Kunden-Tabelle hat. Für jede Gruppe von Zeilen, für die Kunden.id den selben Wert hat, muss zwangsläufig auch Kunden.name denselben Wert haben. Der SQL-Standard achtet sehr darauf, die bekannten funktionalen Abhängigkeiten zu dokumentieren, stellt es aber den Umsetzungen frei, diese zu ignorieren. T301, „Functional dependencies“ ist eine optionale Funktion von SQL.

Db2 (LUW)DerbyH2MariaDBMySQLOracle DBPostgreSQLSQL ServerSQLite2005200720092011201320152017201920212023⚡ 3.5.7 - 3.50.0a⊘ 2008R2 - 2022✓ 9.1 - 17⊘ 8.4 - 9.0⊘ 11gR1 - 23.9✓ 5.7 - 9.3.0a⚡ 5.0 - 5.6a⚡ 5.1 - 12.0.2a⚡ 1.4.192 - 2.3.232a⊘ 10.15.1.3 - 10.17.1.0⊘ 9.7 - 12.1.2
  1. ⚡ Schlägt nicht fehl, wenn keine funktionale Abhängigkeit besteht (per Voreinstellung)

Dieser Aspekt wird von den Herstellern jedoch stark vernachlässigt. Einige Systeme liefern für die Abfrage einen Fehler, was lediglich bedeutet, dass sie die optionale Funktion nicht umsetzen. Andere Systeme (⚡) ignorieren das Problem und erlauben es, beliebige Spalten direkt auszugeben – auch jene, die keine funktionale Abhängigkeit haben. Diese Systeme implizieren also Any_value.

Das bedeutet für SQL-Nutzer vor allem folgendes: Obwohl die Abfrage logisch korrekt ist, könnte sie einen Fehler auslösen (kein T301) oder versehentlich das richtige Ergebnis liefern (impliziert Any_value). Nur zwei von neun getesteten Systemen liefern das gewünschte Verhalten aus dem richtigen Grund. Das ist nicht unbedingt eine Wette, die ich eingehen möchte.

SQL-92 hatte zwei Möglichkeiten, die Abfrage so zu verfassen, dass sie überall richtig funktioniert: (1) funktional abhängige Spalten, die nicht in der Group by-Klausel aufscheinen, über Min oder Max abfragen; (2) die Group by-Klausel um diese Spalten erweitern. Die SQL:2023 Funktion Any_value ist lediglich eine dritte Variante von Option (1). Keine dieser Möglichkeiten ist Benutzerfreundlich. Schlimmer noch: Sie schützen nicht vor Fehlern wie zum Beispiel wenn man eine Spalte irrtümlich in die Group by-Klausel aufnimmt.1

SQL:1999 hat aber eine weitere Möglichkeit eingeführt, die alle genannten Probleme löst: Das (optionale) Feature T301, „Functional dependencies“. Daher möchte ich diese Artikel mit einem Appell an die Hersteller abschließen: Bitte Mehraufwand auf euch und setzt lieber T301 als T626 (any_value) um. Das ist das, was man sich als Benutzer erwartet: kein Aufwand, maximale Sicherheit. An dieser Stelle gebührt dem MySQL-Team für die Umsetzung in Version 5.7 ein großer Dank!

Der einzige Wermutstropfen ist, dass man zum erkennen funktionaler Abhängigkeiten Constraints braucht. Systeme ohne Constraints, wie zum Beispiel ein Data Warehouse, können eben nicht davon profitieren. Für diesen Fall wäre vielleicht eine Funktion wünschenswert, die prüft ob es nur einen nicht-null Wert gibt. Vielleicht Unique_value(… [null|error] on error)? Oder Not enforced trusted Constraints wie bei Db2?

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. Sowie distinct und all, aber das ist sinnlos.

  2. Oder eine der Kunden-Spalten später in die Order-Tabelle wandert.

Neuigkeiten Abonnieren

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