知识卡片

SUMMARIZE与GROUP BY/HAVING的逻辑冗余性及陷阱

操作参考卡

内容

汇总(summarization)区别于[[SQL聚集运算符的三重缺陷]]中的单值聚集,它对 关系r1按r2(r1的某个投影)分组、逐组计算聚集值,产生每组一行的结果。Tutorial D的SUMMARIZE SP PER(S{SNO}):{PCT:=COUNT(PNO)}对应SQL的 SELECT SNO,COUNT(ALL PNO) AS PCT FROM SP GROUP BY SNO,但要留意PER关系和 BY简写(BY{SNO}PER(SP{SNO})的简写)指定的分组来源不同会导致结果不同—— PER(S{SNO})会包含S5这个完全没有出货记录的供应商(结果PCT=0),而BY{SNO} 只对SP本身做分组、天然遗漏S5,这个差异对应到SQL里,就是”FROM S”(对S逐行做 标量子查询汇总)和”FROM SP…GROUP BY SNO”(对SP分组)这两种写法结果不同的 根本原因,选错了汇总对象(该汇总S却写成汇总SP)是使用GROUP BY最容易踩的坑。 书中进一步证明GROUP BY和HAVING在SQL里都是逻辑冗余的构造——任何用GROUP BY/ HAVING表达的查询,都能改写成不用它们的等价形式(用相关子查询在SELECT列表里 逐行计算聚集值),但这不代表GROUP BY/HAVING没有价值,而是提醒:(1)用GROUP BY对某个非分组列取值时(如同时SELECT CITY但只GROUP BY SNO),必须依赖表已 知的函数依赖(S里SNO唯一决定CITY)才合法,SQL标准专门规定了这种”函数依赖 豁免”规则;(2)用SUM等聚集时若某分组恰好聚集在空集合上(比如汇总S5的出货 总量却没有任何出货记录),SQL会返回null而非期望的0,此时必须套COALESCE补救 (呼应[[避免null的实践规则NOT_NULL约束与COALESCE]]);(3)HAVING子句里 必须重复完整的聚集表达式(如HAVING SUM(QTY)>250),不能直接引用SELECT 里起的别名(如误写HAVING TOTQ>250),这是使用GROUP BY/HAVING最容易犯的 语法陷阱之一。

参考来源

- 位置:《SQL与关系数据库理论——如何编写健壮的SQL代码》第7章"SQL和关系代数 II:附加运算符"7.8节"汇总"(源文件:OEBPS/text00081.html) - 结论依据:原文明确"BY{SNO}定义为PER(SP{SNO})的缩写……关系变量SP并不 包含S5供应商的元组……SQL的GROUP BY子句实际上是逻辑冗余的,任何使用GROUP BY的表达式也可以不用GROUP BY来表达……HAVING像GROUP BY一样是逻辑冗余的 ……如果要用GROUP BY或HAVING,那么请确保所汇总的表确实是你想汇总的那个 ……而且,还要注意汇总作用于空集的可能情况,在必要的地方要使用COALESCE"。 - 原始内容:SQL的GROUP BY子句实际上是逻辑冗余的……HAVING像GROUP BY一样是 逻辑冗余的……如果要用GROUP BY或HAVING,那么请确保所汇总的表确实是你想 汇总的那个……还要注意汇总作用于空集的可能情况,在必要的地方要使用 COALESCE。