Pourquoi ANSI SQL définit SUM (pas de lignes) comme NULL?


28

La norme ANSI SQL définit (chapitre 6.5, spécification des fonctions de définition) le comportement suivant pour les fonctions d'agrégation sur des jeux de résultats vides:

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

Renvoyer NULL pour AVG, MIN et MAX est parfaitement logique, car la moyenne, le minimum et le maximum d'un ensemble vide ne sont pas définis.

Le dernier, cependant, me dérange: Mathématiquement, la somme d'un ensemble vide est bien défini: 0. L'utilisation de 0, l' élément neutre de l'addition, comme cas de base rend tout cohérent:

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

Définir SUM({})comme nullfait essentiellement "pas de lignes" un cas spécial qui ne correspond pas aux autres:

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

Y a-t-il un avantage évident du choix qui a été fait (SUM étant NULL) que j'ai manqué?



5
Oui, je suis d'accord: COUNT et SUM ne se comportent pas de manière cohérente.
AK

Réponses:


20

Je crains que la raison en soit simplement que les règles ont été définies de manière ad hoc (comme beaucoup d'autres "fonctionnalités" de la norme ISO SQL) à une époque où les agrégations SQL et leur connexion avec les mathématiques étaient moins comprises qu'elles ne le sont maintenant. (*).

Ce n'est qu'une des nombreuses incohérences du langage SQL. Ils rendent la langue plus difficile à enseigner, plus difficile à apprendre, plus difficile à comprendre, plus difficile à utiliser, plus difficile à tout ce que vous voulez, mais c'est exactement ainsi que les choses sont. Les règles ne peuvent pas être changées "à froid" et "juste comme ça", pour des raisons évidentes de rétrocompatibilité (si le comité ISO publie une version finale de la norme, et que les fournisseurs se mettent ensuite à appliquer cette norme, alors ces fournisseurs n'apprécieront pas c'est très bien si dans une version ultérieure, les règles sont modifiées de telle sorte que les implémentations existantes (conformes) de l'ancienne version de la norme "ne parviennent pas automatiquement à se conformer" à la nouvelle version ...)

(*) Il est désormais mieux compris que les agrégations sur un ensemble vide se comportent de manière plus cohérente si elles renvoient systématiquement la valeur d'identité (= ce que vous appelez «l'élément neutre») de l'opérateur binaire sous-jacent à portée de main. Cet opérateur binaire sous-jacent pour COUNT et SUM est un ajout et sa valeur d'identité est zéro. Pour MIN et MAX, cette valeur d'identité est respectivement la valeur la plus élevée et la plus basse du type en question, si les types concernés sont finis. Des cas comme la moyenne, les moyennes harmoniques, les médianes, etc. sont cependant extrêmement complexes et exotiques à cet égard.


Je pense que null a du sens sur un ensemble vide avec min et max. Vous pourriez dire qu'une valeur d'identité là-bas est vraiment inconnue, mais la somme d'aucune valeur est 0 pour la même raison que n * 0 est toujours 0. Mais min et max sont différents. Je ne pense pas que le résultat soit correctement défini et ne traverse aucun enregistrement.
Chris Travers

De même, avg () sur un ensemble nul a du sens en tant que null car 0/0 n'est pas correctement défini dans ce contexte.
Chris Travers

5
MIN et MAX ne sont pas si différents. Prenez un opérateur binaire sous-jacent respectivement LOWESTOF (x, y) et HIGHESTOF (x, y). Ces opérateurs binaires ont une valeur d'identité. Car dans les deux cas (si le type impliqué est fini), il existe en effet une valeur z telle que forall x: LOWESTOF (z, x) = x et forall y: HIGHESTOF (y, z) = y. (La valeur d'identité n'est pas la même pour les deux cas, mais elle existe pour les deux cas.) Je conviens que les résultats semblent extrêmement contre-intuitifs à première vue, mais on ne peut nier la réalité mathématique.
Erwin Smout

@Erwin: Je suis d'accord sur tous vos points, sauf que l'identité de certaines opérations, comme HIGHEST()beaucoup ne sont pas un élément du type de données, comme pour Reals où l'identité serait le -Infinity(et +Infinitypour LOWEST())
ypercubeᵀᴹ

1
@SQL kiwi. Oubliez-vous la vérification de type statique? Si des expressions comme SUM () sont gérées par le vérificateur de type statique comme si elles retournent toujours un entier, alors il devrait être impossible pour l'invocation SUM () de renvoyer parfois quelque chose qui n'est pas un entier (par exemple une relation vide).
Erwin Smout

3

Dans un sens pragmatique, le résultat existant de NULLest utile. Considérez le tableau et les instructions suivants:

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

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

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

La première instruction renvoie NULL et la seconde renvoie zéro. Si un ensemble vide renvoyait zéro car SUMnous aurions besoin d'un autre moyen pour distinguer une vraie somme de zéro d'un ensemble vide, peut-être en utilisant count. Si nous voulons en effet zéro pour l'ensemble vide, un simple COALESCEfournira cette exigence.

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

1
en conséquence., SUM (union de set1 et set2) <> SUM (set1) + SUM (set2), car tout nombre + NULL = NULL. est-ce que vous saisissez?
AK

2
@Leigh: Une utilisation COALESCE()comme celle-ci ne distinguera pas la 0somme ( ) d'un ensemble vide de la NULLsomme () (disons que la table avait une (10, NULL)ligne.
ypercubeᵀᴹ

En outre, nous ne pouvons toujours pas distinguer SUM (ensemble vide) de SUM (ensemble d'un ou plusieurs NULL). Faut-il distinguer du tout?
AK

@AlexKuznetsov - Nous pouvons distinguer la somme d'un ensemble vide d'une somme d'un ensemble qui contient un ou plusieurs nulls tant qu'au moins une ligne contient une valeur. Vous avez raison de dire que si l'ensemble ne contient que des valeurs NULL, nous ne pouvons pas distinguer l'ensemble NULL de cet ensemble de toutes les valeurs NULL. Mon point n'était pas qu'il est utile dans tous les cas, mais simplement qu'il peut être utile. Si j'ai SUMune colonne et que je reviens à zéro, je le sais sans avoir à vérifier qu'il y a au moins une ligne non NULL utilisée pour me montrer le résultat.
Leigh Riffel

@ypercude - Vous avez absolument raison. Mon point était que le comportement actuel de SUM distingue un ensemble vide d'un ensemble qui contient des valeurs (même si certains sont nuls). Il est plus simple d'utiliser COALESCE lorsque la distinction n'est pas requise que d'utiliser quelque chose comme DECODE(count(c2),0,NULL,sum(c2))quand il l'est.
Leigh Riffel

-1

La principale différence que je peux voir concerne le type de données. COUNT a un type de retour bien défini: un nombre entier. Tous les autres dépendent du type de colonne / expression qu'ils regardent. Leur type de retour doit être compatible avec tous les membres de l'ensemble (think float, currency, decimal, bcd, timepan, ...). Puisqu'il n'y a aucun ensemble, vous ne pouvez pas impliquer un type de retour, donc NULL est votre meilleure option.

Remarque: Dans la plupart des cas, vous pouvez impliquer un type de retour à partir du type de colonne que vous regardez, mais vous pouvez faire des SUM non seulement sur des colonnes mais sur toutes sortes de choses. Impliquer un type de retour peut devenir très difficile, voire impossible, dans certaines circonstances, en particulier lorsque vous pensez à des extensions possibles de la norme (les types dynamiques viennent à l'esprit).


5
Pourquoi ne pouvons-nous pas impliquer un type de retour dans une SUM(column)expression? N'avons-nous pas des tables vides - et là toutes les colonnes ont des types définis? Pourquoi devrait-il en être différemment pour un jeu de résultats vide?
ypercubeᵀᴹ

5
Vous vous trompez lorsque vous dites "car il n'y a pas de set ". Il y a un ensemble. L'ensemble de toutes les valeurs possibles du type déclaré des colonnes ou expressions impliquées. Ce type déclaré existe même si la table que vous regardez est vide. Même les tables vides ont toujours un en-tête. Et ce type déclaré est exactement votre "type de retour implicite".
Erwin Smout

Avez-vous tous les deux lu ma note? Oui, cela fonctionnerait pour les SUM basés sur des colonnes à partir de maintenant. Mais dès que vous rencontrez une colonne de type de données variable (pas encore dans SQL Server), vous n'avez pas de chance.
TToni

2
Comment définirez-vous la somme dans ce cas? Quel sera le résultat 24 + 56.07 + '2012-10-05' + 'Red'? Je veux dire qu'il n'y a aucune pinte à s'inquiéter de la façon dont SUM()se comportera lorsque nous aurons un problème à définir l'ajout.
ypercubeᵀᴹ

1
@TToni: "surtout quand vous pensez à des extensions possibles de la norme" n'est pas le contexte auquel l'OP faisait référence. le PO faisait très clairement référence à la version actuelle de la norme, qui n'inclut aucune sorte de notion de "types dynamiques" ou quelque chose du genre. (Oh, et je n'ai fait que commenter, mais je n'ai pas dévalué. Mis à part ce petit glissement avec lequel je me suis opposé, rien dans votre réponse n'était suffisamment faux pour justifier un vote négatif. OMI.)
Erwin Smout
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.