知识卡片
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最容易犯的
语法陷阱之一。