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)?
恐怕原因很简单,在 SQL 聚合及其与数学的联系不如现在理解的时候,这些规则是以一种临时方式设置的(就像ISO SQL 标准的许多其他“特性”一样) (*)。
这只是 SQL 语言中极多的不一致之处之一。它们使语言更难教、更难学习、更难理解、更难使用、更难实现你想要的任何东西,但事情就是这样。出于向后兼容性的明显原因,规则不能“冷”和“就那样”更改(如果 ISO 委员会发布标准的最终版本,然后供应商开始实施该标准,那么这些供应商将不会欣赏如果在后续版本中,规则会发生更改,使得标准的前一版本的现有(兼容)实现“自动不符合”新版本......)
(*) 现在可以更好地理解,如果它们系统地返回手头底层二元运算符的标识值(= 你所谓的“中性元素”),则空集上的聚合行为会更加一致。COUNT 和 SUM 的底层二元运算符是加法,其标识值为零。对于 MIN 和 MAX,如果相关类型是有限的,则该标识值分别是当前类型的最高和最低值。不过,在这方面,平均、调和平均值、中位数等情况非常复杂和奇特。
在务实的意义上,现有的结果
NULL
是有用的。考虑下表和语句:第一条语句返回 NULL,第二条语句返回 0。如果一个空集返回零,
SUM
我们将需要另一种方法来区分真正的零和与空集,也许使用计数。如果我们确实希望空集为零,那么一个简单的COALESCE
函数将满足该要求。我可以看到的主要区别在于数据类型。COUNT 有一个明确定义的返回类型:一个整数。所有其他都取决于他们正在查看的列/表达式的类型。它们的返回类型必须与集合的所有成员兼容(想想浮点数、货币、小数、bcd、时间跨度……)。由于没有集合,您不能暗示返回类型,因此 NULL 是您的最佳选择。
注意:在大多数情况下,您可以从正在查看的列类型中暗示返回类型,但是您不仅可以对列进行求和,还可以对所有类型的事物进行求和。在某些情况下,如果不是不可能的话,暗示返回类型可能会变得非常困难,尤其是当您考虑标准的可能扩展时(想到动态类型)。