知识卡片

SQL空表约束自动满足的陷阱

普通读书笔记卡

内容

Tutorial D通过CONSTRAINT语句声明数据库约束,SQL通过CREATE ASSERTION语句 表达等价内容,此外SQL还额外提供基表约束(作为CREATE TABLE定义一部分书写) 这种更简洁的替代写法——单表的元组约束(如CX1、CX2)适合用基表约束表达, 涉及多表的约束(如CX5)用CREATE ASSERTION更合适(避免纠结该把约束挂在 哪个表上)。但基表约束有一个容易被忽视的陷阱:任何作为CREATE TABLE语句 一部分声明的约束,在对应表恰好是空表的情况下会自动被判定为满足——即便约束 写的字面意思是”T不能为空”,甚至是”T必须恰好包含-5行”或者直接写”1=0”这种 恒假表达式,只要表本身当前是空的,这条约束在SQL的语义下依然被判定为满足。 这个反直觉的行为进一步暴露了当前SQL产品在约束支持能力上的真实短板:绝大 多数产品虽然支持键约束和外键约束,却普遍不支持CREATE ASSERTION,也不支持 比简单单表行约束更复杂的基表约束(正式说法是不允许基表约束里包含子查询) ——涉及多表的复杂约束在实践中往往只能退而求其次,靠过程式代码(比如触发 过程)去实现,而这类代码普遍公认难写、易错,是当前产品在完整性支持方面 一个长期未被纠正的重大缺陷。

参考来源

- 位置:《SQL与关系数据库理论——如何编写健壮的SQL代码》第8章"SQL与约束" 8.4节"SQL中的数据库约束"(源文件:OEBPS/text00092.html) - 结论依据:原文明确"作为表T的CREATE TABLE语句组成部分进行声明的任何约束, 在T为空表的情况下是自动满足的——就算约束形式为'T不能为空'也不例外!…… 尽管当前的大多数SQL产品都支持键约束和外键约束,但是它们都不支持CREATE ASSERTION,它们也不支持比简单行约束更复杂的基表约束……很多(大部分) 约束都遗憾地通过过程式代码……来执行,并且过程代码还相当难写"。 - 原始内容:作为表T的CREATE TABLE语句组成部分进行声明的任何约束,在T为 空表的情况下是自动满足的——就算约束形式为"T不能为空"也不例外……大多数 SQL产品……都不支持CREATE ASSERTION……很多约束都遗憾地通过过程式代码 来执行,并且过程代码还相当难写。