知识卡片

GROUP BY/HAVING与显式演算表述在"零匹配"场景下的语义差异

普通读书笔记卡

内容

查询”每种由不超过2个供应商供应的零件,得到其编号、城市、总供应量”,用 [[关系演算的基本结构区间变元原型元组WHERE谓词]]标准套路能推导出一个不依赖 GROUP BY的SQL表达式(对每个零件PX,用相关子查询算COUNT和COALESCE过的SUM, 再在WHERE里判断COUNT<=2);但用GROUP BY/HAVING改写后更简洁: SELECT PX.PNO,PX.CITY,COALESCE(SUM(SPX.QTY),0)AS TPQ FROM P AS PX,SP AS SPX WHERE PX.PNO=SPX.PNO GROUP BY PX.PNO HAVING COUNT(*)<=2。这个 对照暴露了一个关键的语义分歧:完全没有被任何供应商供应的零件型号,在 GROUP BY/HAVING版本里根本不会出现在结果里(因为FROM子句的JOIN先天就把 这类零件过滤掉了),但按照原始查询字面意思,一个”由0个供应商供应”的零件 显然满足”不超过2个供应商供应”这个条件,理应出现在结果集里——GROUP BY/ HAVING这条更简洁的写法因此和原始查询意图并不完全一致,这正呼应了 [[SUMMARIZE与GROUP_BY_HAVING的逻辑冗余性及陷阱]]里”用GROUP BY前必须 先确认汇总对象选对了”这条实践建议:这里真正该汇总的对象应该是P(所有 零件,包括未被供应的),而GROUP BY版本实际上是在对JOIN之后、已经把无 供应零件排除掉的中间结果做分组,选错了汇总对象。这个案例是本章”简洁写法 未必等价于精确写法”这条主题的又一次具体印证——SELECT子句里出现的PX.CITY 虽然不是分组列,但当代SQL标准允许它出现是因为它函数依赖于分组列PX.PNO (呼应[[函数依赖的精确定义及为何通常不需要专门声明]]),这条规则本身 在早期SQL版本里并不成立,SELECT项曾经被要求必须严格是分组列本身。

参考来源

- 位置:《SQL与关系数据库理论——如何编写健壮的SQL代码》第11章"使用逻辑 表述SQL表达式"11.13节"例12:GROUP BY和HAVING"(源文件:OEBPS/text00131.html) - 结论依据:原文明确"用GROUP BY和HAVING似乎更容易表达(肯定更简洁)…… 对于未由任何供应商供应的零件型号,此GROUP BY/HAVING表述方式会正确 运行吗?(不会。)""设S是具有GROUP BY子句的SELECT表达式,设S的SELECT 子句引用了列C……在当前版本的SQL中,C(或{C})只要函数依赖于分组列 即可"。 - 原始内容:对于未由任何供应商供应的零件型号,此GROUP BY/HAVING表述 方式会正确运行吗?(不会。)……C只要函数依赖于分组列即可。