知识卡片

SQL聚集运算符的三重缺陷

普通读书笔记卡

内容

关系模型的聚集运算符(COUNT/SUM/AVG/MAX/MIN/AND/OR/XOR)从某关系的某属性 “聚集”出一个标量结果,Tutorial D里SUM(SP,QTY)这类调用能作为独立表达式直接 出现。SQL的对应写法看似做同样的事,实则存在三重缺陷:第一,SQL其实根本不 支持真正的聚集运算符——SELECT COUNT(*) FROM S这类表达式得到的不是一个标量, 而是一个只有一行一列的表,只是这个表恰好又被隐式型转成它包含的那个标量值, 本质上SQL把聚集当成汇总(GROUP BY形式,见[[SUMMARIZE与GROUP_BY_HAVING的逻辑 冗余性及陷阱]])的特例来处理,SELECT COUNT(*) AS X FROM S实际是 ...GROUP BY()(空分组列表)的缩写,这个”表→行→标量”的双重型转掩盖了SQL 的”聚集”和真正意义上聚集运算符之间的本质差异。第二,[[类型的正式定义与类型 生成器]]中已知SUM/COUNT/AVG等在Tutorial D里对空参数有干净的恒等值语义(0对 加法恒等所以SUM空集合理应为0,TRUE对AND恒等,MAX/MIN对空集无意义应报异常), 但对应的SQL集合函数在参数为空时几乎全部错误地返回null(唯独COUNT和COUNT(*) 正确返回0),这直接呼应[[避免null的实践规则NOT_NULL约束与COALESCE]]里必须 套COALESCE处理空聚集结果的实践规则,根源正是这里。第三,SQL标准对”空表GROUP BY”存在一条特设规则打破了自身逻辑一致性:一般而言,一个空表分组后应当得到 0个分组(因此最终结果也是空),但SQL标准明确规定”没有分组列时,把整张表T 当作唯一一个分组”,导致对空表S执行GROUP BY()反而得到唯一一个分组(这个组 本身为空),COUNT(*)对这个空组求值返回0——这个特例本身”看起来更符合直觉”, 但书中引用Wittgenstein”所有逻辑区别都是大区别”,强调这种为了追求实用结果而 悄悄破坏逻辑一致性的做法,对任何严格建立在逻辑基础上的系统而言都是不可接受的 设计瑕疵。

参考来源

- 位置:《SQL与关系数据库理论——如何编写健壮的SQL代码》第7章"SQL和关系代数 II:附加运算符"7.6节"聚集运算符"(源文件:OEBPS/text00079.html) - 结论依据:原文明确"SQL实际上根本并不支持聚集运算符!……每个都是得到一个 包含一行一列的表……SQL把聚集当作汇总(summarization)的特例看待""这些运算符 的SQL对应项在参数为空的情况下都返回null(除了COUNT和COUNT(*)……正确返回了 0)""SQL在分组一个空表时实际上……产生一个组的空集,而分组列集合为空的情况 是一个特例……再次引述Wittgenstein的话:所有的逻辑区别都是大区别"。 - 原始内容:SQL实际上根本并不支持聚集运算符!……SQL把聚集当作汇总的特例 看待……这些运算符的SQL对应项在参数为空的情况下都返回null……所有的逻辑 区别都是大区别。