为什么ANSI SQL将SUM(无行)定义为NULL?


28

ANSI SQL标准定义(第6.5章,集功能规范),用于空的结果集的集合函数以下行为:

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

由于未定义空集的平均值,最小值和最大值,因此对AVG,MIN和MAX返回NULL十分合理。

但是,最后一个让我感到困扰:从数学上来说,空集的SUM是定义明确的:0。使用0 (加法的中性元素)作为基本情况可以使所有内容保持一致:

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

基本上定义SUM({})null“无行”是一种特殊情况,不适合其他情况:

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

我错过的选择(SUM为NULL)有明显的优势吗?



5
是的,我同意:COUNT和SUM的行为不一致。
AK

Answers:


20

恐怕原因仅在于,在SQL聚合及其与数学的联系不如现在理解的时候,这些规则是以一种即席方式设置的(就像ISO SQL标准的许多其他“功能”一样) (*)。

它只是SQL语言中极为众多的不一致之一。它们使语言变得更难教,更难学,更难理解,更难使用,更难于您想要的任何东西,但这就是事实。出于向后兼容的明显原因,不能将规则“冷”和“正好那样”更改(如果ISO委员会发布了该标准的最终版本,而供应商随后着手实施该标准,那么那些供应商将不会感激。如果在随后的版本中更改了规则,那么该标准的以前版本的现有(兼容)实现会“自动未能遵守”新版本...)

(*)现在可以更好地理解,如果空集上的聚合系统地返回手头基础二进制运算符的标识值(即您所谓的“中性元素”),则其行为会更加一致。COUNT和SUM的基础二进制运算符是加法,其标识值为零。对于MIN和MAX,如果所关注的类型是有限的,则该标识值分别是当前类型的最高值和最低值。但是,在这方面,平均,谐波均值,中位数等情况极为复杂且奇特。


我认为null对具有min和max的空集有意义。您可能会说一个身份值确实是未知的,但由于n * 0始终为0的相同原因,无值之和为0。但是min和max不同。我认为没有在任何记录上正确定义结果。
克里斯·特拉弗斯

同样,在空集上的avg()也可以视为空值,因为在此上下文中未正确定义0/0。
克里斯·特拉弗斯

5
MIN和MAX差别不大。分别采用基础二进制运算符LOWESTOF(x,y)和HIGHESTOF(x,y)。这些二进制运算符确实具有标识值。因为在两种情况下(如果所涉及的类型是有限的),实际上都存在一些值z,使得forall x:LOWESTOF(z,x)= x和forall y:HIGHESTOF(y,z)= y。(两种情况下的身份值并不相同,但两种情况下都存在。)我同意,乍一看结果似乎非常违反直觉,但不可否认数学上的现实。
Erwin Smout,2012年

@Erwin:我同意您所有的点,除了一些操作,如身份HIGHEST()很多不是数据类型的元素,像雷亚尔在身份将是-Infinity(和+InfinityLOWEST()
ypercubeᵀᴹ

1
@SQL猕猴桃。您是否忘记了静态类型检查?如果像SUM表达式()由静态类型检查处理,就好像它们总是返回一个整数,那么显然应该是不可能的SUM()调用有时返回的东西是不是一个整数(如一个空的关系)。
Erwin Smout,2012年

3

在务实的意义上,的现有结果NULL是有用的。请考虑以下表格和语句:

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

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

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

第一条语句返回NULL,第二条语句返回零。如果一个空集返回零,SUM则可能需要使用count这样的另一种方法来区分一个空集的零和为零。如果我们确实希望空集为零,那么COALESCE将提供一个简单的要求。

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

1
结果是,SUM(set1和set2的联合)<> SUM(set1)+ SUM(set2),因为任何数字+ NULL = NULL。这对您有意义吗?
AK 2012年

2
@Leigh:这样使用COALESCE()不会将0空集的(NULL)和与()和(例如表中有(10, NULL)一行。)
ypercubeᵀᴹ2012年

此外,我们仍然无法区分SUM(空集)和SUM(一个或多个NULL集)。我们是否需要区分?
AK 2012年

@AlexKuznetsov-只要至少一行包含一个值,我们就可以将一个空集的总和与包含一个或多个空值的集合的总和区分开。您是正确的,如果该集合仅包含NULL,那么我们无法将NULL集合与所有NULL值的集合区分开。我的意思并不是说它在每种情况下都是有用的,仅仅是它可以是有用的。如果我将SUM一列返回零,则无需检查是否至少有一个非NULL行用于显示结果,所以我知道。
雷·里菲尔

@ypercude-您完全正确。我的观点是,SUM的当前行为确实将空集与包含值的集区分开(即使某些值为空)。当不需要区分时,使用COALESCE比使用区分时更简单DECODE(count(c2),0,NULL,sum(c2))
雷·里菲尔

-1

我可以看到的主要区别在于数据类型。COUNT的返回类型定义明确:整数。所有其他依赖于他们正在查看的列/表达式的类型。它们的返回类型必须与集合的所有成员兼容(请考虑浮点数,货币,十进制,bcd,时间跨度,...)。由于没有设置,因此您不能暗示返回类型,因此NULL是最佳选择。

注意:在大多数情况下,您可能暗示所查看的列类型为返回类型,但您不仅可以对列进行求和,而且还可以对所有事情进行求和。在某些情况下,即使不是不可能,暗示返回类型可能会变得非常困难,尤其是当您考虑标准的可能扩展时(想到动态类型)。


5
为什么我们不能在SUM(column)表达式中隐含返回类型?我们没有空表吗?所有的列都有定义的类型吗?为什么对空结果集应该有什么不同?
ypercubeᵀᴹ

5
您在说“因为没有设置 ”的地方弄错了。有一套。涉及的列或表达式的声明类型的所有可能值的集合。即使您正在查看的表为空,该声明的类型也存在。即使是空表也有标题。并且该声明的类型正是您的“隐式返回类型”。
Erwin Smout,2012年

你们俩都看过我的笔记吗?是的,到目前为止,它适用于基于列的SUM。但是,一旦遇到可变数据类型列(不在SQL Server中),您就很不走运。
TToni 2012年

2
在这种情况下,您将如何定义总和?结果将24 + 56.07 + '2012-10-05' + 'Red'是什么?我的意思是,在SUM()定义加法时遇到问题时,不必担心会如何表现。
ypercubeᵀᴹ

1
@TToni:“尤其是当您考虑标准的可能扩展时”并不是OP所指的上下文。OP非常清楚地指的是该标准的当前版本,该版本不包含任何“动态类型”或类似概念。(哦,我只评论,但并没有从微小的滑我把问题与downvote除此之外,没有在你的答案是错的,足以保证一个downvote IMO。)
欧文斯莫特
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.