Warum definiert ANSI SQL SUM (keine Zeilen) als NULL?

28

Der ANSI-SQL-Standard definiert (Kapitel 6.5, Festlegen der Funktionsspezifikation) das folgende Verhalten für Aggregatfunktionen in leeren Ergebnismengen:

COUNT(...) = 0
AVG(...) = NULL
MIN(...) = NULL
MAX(...) = NULL
SUM(...) = NULL

Die Rückgabe von NULL für AVG, MIN und MAX ist absolut sinnvoll, da der Durchschnitt, das Minimum und das Maximum einer leeren Menge undefiniert sind.

Die letzte, aber ich stört: Mathematisch die Summe von einer leeren Menge wohldefiniert: 0. Mit 0, dem neutralen Element der Addition, als Basisfall wird alles konsistent:

SUM({})        = 0    = 0
SUM({5})       = 5    = 0 + 5
SUM({5, 3})    = 8    = 0 + 5 + 3
SUM({5, NULL}) = NULL = 0 + 5 + NULL

Wenn Sie "keine Zeilen" SUM({})als " nullgrundsätzlich" definieren, ist dies ein Sonderfall, der nicht zu den anderen passt:

SUM({})     = NULL  = NULL
SUM({5})    = 5    != NULL + 5 (= NULL)
SUM({5, 3}) = 8    != NULL + 5 + 3 (= NULL)

Gibt es einen offensichtlichen Vorteil der getroffenen Wahl (SUMME ist NULL), den ich verpasst habe?

Heinzi
quelle
Hinweis: Dies ist eine verallgemeinerte Version einer Frage, die ich in StackOverflow speziell zu SQL Server gestellt habe .
Heinzi
5
Ja, ich stimme zu: COUNT und SUM verhalten sich nicht konsistent.
AK

Antworten:

20

Ich befürchte, der Grund dafür liegt einfach darin, dass die Regeln ad-hoc festgelegt wurden (wie viele andere "Funktionen" des ISO- SQL-Standards), als SQL-Aggregationen und ihre Verbindung zur Mathematik weniger verstanden wurden als heute (*).

Dies ist nur eine der extrem vielen Inkonsistenzen in der SQL-Sprache. Sie erschweren das Unterrichten der Sprache, das Lernen, das Verstehen, die Verwendung und die Auswahl der gewünschten Sprache, aber genau so sind die Dinge. Die Regeln können aus offensichtlichen Gründen der Abwärtskompatibilität nicht "kalt" und "einfach so" geändert werden Es ist sehr wichtig, dass in einer späteren Version die Regeln so geändert werden, dass vorhandene (konforme) Implementierungen der früheren Version des Standards die neue Version "automatisch nicht erfüllen" ...)

(*) Es ist jetzt verständlicher, dass sich Aggregationen über eine leere Menge konsistenter verhalten, wenn sie systematisch den Identitätswert (= das, was Sie das "neutrale Element" nennen) des zugrunde liegenden Binäroperators zurückgeben. Der zugrunde liegende Binäroperator für COUNT und SUM ist die Addition, und sein Identitätswert ist Null. Für MIN und MAX ist dieser Identitätswert der höchste und der niedrigste Wert des jeweiligen Typs, wenn die betroffenen Typen endlich sind. Fälle wie Mittelwertbildung, harmonische Mittelwerte, Mediane usw. sind in dieser Hinsicht jedoch äußerst kompliziert und exotisch.

Erwin Smout
quelle
Ich denke null macht Sinn über eine leere Menge mit min und max. Man könnte sagen, ein Identitätswert ist dort wirklich unbekannt, aber die Summe der Nullwerte ist 0, aus dem gleichen Grund, dass n * 0 immer 0 ist. Aber min und max sind unterschiedlich. Ich denke nicht, dass das Ergebnis korrekt definiert ist und über keine Datensätze läuft.
Chris Travers
Auch avg () über eine Nullmenge ist als Null sinnvoll, da 0/0 in diesem Kontext nicht richtig definiert ist.
Chris Travers
5
MIN und MAX sind nicht so unterschiedlich. Nehmen Sie einen zugrunde liegenden binären Operator LOWESTOF (x, y) bzw. HIGHESTOF (x, y). Diese binären Operatoren haben einen Identitätswert. Denn in beiden Fällen (wenn der beteiligte Typ endlich ist) gibt es tatsächlich einen Wert z, der für alle x: LOWESTOF (z, x) = x und für alle y: HIGHESTOF (y, z) = y gilt. (Der Identitätswert ist in beiden Fällen nicht derselbe, aber er existiert in beiden Fällen.) Ich stimme zu, dass die Ergebnisse auf den ersten Blick äußerst eingängig wirken, aber die mathematische Realität lässt sich nicht leugnen.
Erwin Smout
@Erwin: Ich auf allen Punkten einig, mit der Ausnahme , dass die Identität einiger Operationen, wie HIGHEST()viele nicht ein Element des Datentypen sein, wie für Reals , wo die Identität der wäre -Infinity(und +Infinityfür LOWEST())
ypercubeᵀᴹ
1
@ SQL Kiwi. Vergessen Sie die statische Typprüfung? Wenn Ausdrücke wie SUM () von der statischen Typprüfung so behandelt werden, als ob sie immer eine Ganzzahl zurückgeben, sollte es für den SUM () - Aufruf offensichtlich unmöglich sein, manchmal etwas zurückzugeben, das keine Ganzzahl ist (z. B. eine leere Beziehung).
Erwin Smout
3

In einem pragmatischen Sinne ist das vorhandene Ergebnis von NULLnützlich. Betrachten Sie die folgende Tabelle und Anweisungen:

C1 C2
-- --
 1  3 
 2 -1 
 3 -2 

SELECT SUM(C2) FROM T1 WHERE C1 > 9;

SELECT SUM(C2) FROM T1 WHERE C1 < 9;

Die erste Anweisung gibt NULL und die zweite null zurück. Wenn eine leere Menge Null SUMzurückgibt, benötigen wir ein anderes Mittel, um eine wahre Summe von Null von einer leeren Menge zu unterscheiden, möglicherweise unter Verwendung von count. Wenn wir in der Tat Null für die leere Menge wollen, dann wird ein einfaches COALESCEdiese Anforderung liefern.

SELECT COALESCE(SUM(C2),0) FROM T1 WHERE C1 > 9;
Leigh Riffel
quelle
1
als Ergebnis., SUM (Vereinigung von Set1 und Set2) <> SUM (Set1) + SUM (Set2), weil eine beliebige Zahl + NULL = NULL. Macht es für dich Sinn?
AK
2
@Leigh: Wenn Sie COALESCE()so verwenden, wird die ( 0) - Summe einer leeren Menge nicht von der ( NULL) - Summe unterschieden (sagen wir, die Tabelle hatte eine (10, NULL)Zeile.
ypercubeᵀᴹ
Außerdem können wir SUM (leere Menge) immer noch nicht von SUM (Menge von einer oder mehreren NULL-Werten) unterscheiden. Müssen wir überhaupt unterscheiden?
AK
@AlexKuznetsov - Wir können eine Summe einer leeren Menge von einer Summe einer Menge unterscheiden, die eine oder mehrere Nullen enthält, solange mindestens eine Zeile einen Wert enthält. Sie haben Recht, wenn die Menge nur NULL-Werte enthält, können wir die NULL-Menge nicht von dieser Menge aller NULL-Werte unterscheiden. Mein Punkt war nicht, dass es in jedem Fall nützlich ist, nur, dass es nützlich sein kann. Wenn ich SUMeine Spalte habe und wieder null bekomme, weiß ich, ohne dass ich überprüfen muss, ob mindestens eine Nicht-NULL-Zeile verwendet wird, um mir das Ergebnis anzuzeigen.
Leigh Riffel
@ypercude - Du bist absolut korrekt. Mein Punkt war, dass das aktuelle Verhalten von SUM eine leere Menge von einer Menge unterscheidet, die Werte enthält (auch wenn einige null sind). Es ist einfacher, COALESCE zu verwenden, wenn die Unterscheidung nicht erforderlich ist, als etwas zu verwenden, wie DECODE(count(c2),0,NULL,sum(c2))es ist.
Leigh Riffel
-1

Der Hauptunterschied, den ich sehe, betrifft den Datentyp. COUNT hat einen genau definierten Rückgabetyp: Eine ganze Zahl. Alle anderen hängen vom Typ der Spalte / des Ausdrucks ab, die / den sie betrachten. Ihr Rückgabetyp muss mit allen Elementen der Menge kompatibel sein (think float, currency, decimal, bcd, timespan, ...). Da es keine Menge gibt, können Sie keinen Rückgabetyp implizieren, daher ist NULL die beste Option.

Anmerkung: In den meisten Fällen können Sie einen Rückgabetyp für den betrachteten Spaltentyp implizieren, aber Sie können SUMs nicht nur für Spalten, sondern für alle Arten von Dingen ausführen. Die Implementierung eines Rückgabetyps kann unter bestimmten Umständen sehr schwierig oder gar unmöglich werden, insbesondere wenn Sie über mögliche Erweiterungen des Standards nachdenken (dynamische Typen fallen Ihnen ein).

TToni
quelle
5
Warum können wir in einem SUM(column)Ausdruck keinen Rückgabetyp implizieren ? Haben wir keine leeren Tabellen - und dort haben alle Spalten definierte Typen? Warum sollte es bei einer leeren Ergebnismenge anders sein?
ypercubeᵀᴹ
5
Sie verstehen es falsch, wenn Sie "da ist KEIN SET " sagen . Es gibt einen Satz. Die Menge aller möglichen Werte des deklarierten Typs der beteiligten Spalten oder Ausdrücke. Dieser deklarierte Typ existiert auch dann, wenn die Tabelle, die Sie betrachten, leer ist. Auch leere Tabellen haben noch eine Überschrift. Und dieser deklarierte Typ ist genau Ihr "impliziter Rückgabetyp".
Erwin Smout
Haben Sie beide meine Notiz gelesen? Ja, es würde ab sofort für spaltenbasierte SUMs funktionieren. Aber sobald Sie auf eine variable Datentyp-Spalte stoßen (noch nicht in SQL Server), haben Sie Pech.
TToni
2
Wie definieren Sie in diesem Fall die Summe? Was wird das Ergebnis 24 + 56.07 + '2012-10-05' + 'Red'sein? Ich meine, es gibt keinen Grund zur Sorge, wie sich SUM()das verhält, wenn wir ein Problem bei der Definition der Addition haben.
Ypercubeᵀᴹ
1
@TToni: "Besonders wenn Sie über mögliche Erweiterungen des Standards nachdenken" ist nicht der Kontext, auf den sich das OP bezog. Das OP bezog sich ganz klar auf die aktuelle Version der Norm, die keinerlei Begriff von "dynamischen Typen" oder dergleichen enthält. (Oh, und ich habe nur kommentiert, aber nicht abgewertet. Abgesehen von diesem winzigen Ausrutscher, den ich beanstandet habe, war nichts in Ihrer Antwort falsch genug, um eine Ablehnung zu rechtfertigen. IMO.)
Erwin Smout