知识卡片

避免重复的实践规则:DISTINCT与ALL的陷阱

操作参考卡

内容

即使每张基表都有键(永不产生重复),SELECT ALL、UNION ALL、VALUES表值构造器 这几种SQL结构仍然可能让结果表带有重复。DISTINCT/ALL可以出现在三个位置:紧跟 SELECT关键字之后;紧跟UNION/INTERSECT/EXCEPT关键字之后;SUM这类”集函数”调用 括号内、参数表达式之前。三个位置的默认值不统一——UNION/INTERSECT/EXCEPT默认 是DISTINCT,其余场合默认是ALL;集函数是特例,如果确实想让重复参与统计就必须 显式(至少隐式)指定ALL。要关系化地使用SQL,理论上的建议很直接:始终显式指定 DISTINCT,永远不要指定ALL,这样就彻底不用操心重复问题。但书中坦承这条建议在 实践中会招来批评——很多真正精通SQL实现的人反感”到处写DISTINCT”,理由是很多 场合下DISTINCT在逻辑上根本是多余的(比如对本身就带键约束的列做SELECT DISTINCT, 或者DISTINCT紧跟着一个已经按同名列GROUP BY的查询),加上不同DISTINCT对性能的 实际影响差异很大,用户没必要背下”SQL在哪些场合会自动消除重复、哪些又不会”这套 不一致的隐性规则。折衷之后给出的实用做法是:先弄清楚SQL在哪些场合会在你没有 明确要求的情况下自动消除重复;对必须要求消除重复的场合,判断如果不消除是否 真的会造成问题;只在确认会有问题的地方显式加DISTINCT;无论如何都不要显式指定 ALL。

参考来源

- 位置:《SQL与关系数据库理论——如何编写健壮的SQL代码》第4章"不要重复,不要 null"4.3节"在SQL中避免重复"(源文件:OEBPS/text00045.html) - 结论依据:原文明确"DISTINCT对于UNION、INTERSECT和EXCEPT是默认的;ALL对于 其他情况是默认的……本书的建议就是:总是指定DISTINCT;宁可显式地这么做;永远 不要指定ALL""首先,确保你知道SQL什么时候会在你未要求的情况下消除重复。其次, 在那些必须要求消除重复的场合,要知道如果不要求消除重复是否会有问题……在会 出现问题的情况下,指定DISTINCT……还有,永远别指定ALL!"。 - 原始内容:DISTINCT对于UNION、INTERSECT和EXCEPT是默认的;ALL对于其他情况是 默认的……总是指定DISTINCT;宁可显式地这么做;永远不要指定ALL……永远别指定 ALL!