知识卡片
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的陷阱]]
里”永远避免重复行”的原则,否则整条约束会悄悄失效。