知识卡片

UNIQUE量词到SQL的映射陷阱

操作参考卡

内容

[[UNIQUE量词及SQL近似实现]]里已知逻辑表达式EXISTS SX(bx)直接映射为 EXISTS(SELECT * FROM S AS SX WHERE(sbx)),但书中特别指出一个”给粗心人 设的陷阱”:逻辑表达式UNIQUE SX(bx)并不能简单照搬同样的模式映射成 UNIQUE(SELECT * FROM S AS SX WHERE(sbx))——这是因为SQL的UNIQUE运算符 本身的语义只判断”参数表里有没有重复行”,本身并不排除”参数表为空”这种情况 (空表和只有一行的表一样,天然不含重复行,UNIQUE都会返回TRUE),而逻辑 上的UNIQUE量词”恰好一个”这个语义,实际上是”至多一个”(UNIQUE的作用)和 “至少一个”(EXISTS的作用)两个条件的合取。正确的映射必须显式拼上EXISTS: UNIQUE(SELECT k FROM S AS SX WHERE(sbx)) AND EXISTS(SELECT * FROM S AS SX WHERE(sbx))(k是任意常量)。以约束CX6(”每笔出货恰好对应一个 供应商”)为例,正确翻译成SQL后得到的CREATE ASSERTION结构相当晦涩,书中 用一句话概括它对应的(同样绕口的)自然语言:”不存在出货满足:既不是至多 有一个对应的供应商,也不是至少有一个对应的供应商”——这句拗口的话恰恰 逻辑等价于直观的”每个出货都恰好有一个对应的供应商”。这个约束还可以进一步 用[[IS_EMPTY_COUNT等价关系及COUNT替代EXISTS的性能陷阱]]里UNIQUE x(bx) ≡COUNT(X WHERE bx)=1这条等价关系化简为一个基于COUNT的更紧凑写法 ((SELECT COUNT(*)FROM S AS SX WHERE SX.SNO=SPX.SNO)<>1),但要留意 :这类基于”计数并与1比较”重写出的表达式,如果依赖表S本身”没有重复行”这条 隐含前提才成立,就必须严格遵循[[避免重复的实践规则DISTINCT与ALL的陷阱]] 里”永远避免重复行”的原则,否则整条约束会悄悄失效。

参考来源

- 位置:《SQL与关系数据库理论——如何编写健壮的SQL代码》第11章"使用逻辑 表述SQL表达式"11.11节"例10:唯一量词化"(源文件:OEBPS/text00129.html) - 结论依据:原文明确"逻辑表达式UNIQUE SX(bx)并不会映射到SQL表达式 UNIQUE(SELECT*FROM S AS SX WHERE(sbx))(这是为粗心的人设置的显而 易见的陷阱)。相反,它会映射为:UNIQUE(SELECT k FROM S AS SX WHERE (sbx)) AND EXISTS(SELECT*FROM S AS SX WHERE(sbx))……这个表述 方式依赖于……禁止出现重复行的事实,否则该表述方式将不成立。避免重复 行!"。 - 原始内容:逻辑表达式UNIQUE SX(bx)并不会映射到SQL表达式UNIQUE(SELECT *FROM S AS SX WHERE(sbx))(这是为粗心的人设置的显而易见的陷阱)…… 这个表述方式依赖于……禁止出现重复行的事实……避免重复行!