在SQL For Smarties中,Joe Celko 提供了 Series 表(在其他地方称为 Tally 或 Numbers)的 ANSI SQL 定义。他的定义确保列中的值是唯一的、正的并且从 1 到最大值是连续的:
CREATE TABLE Series (
seq INTEGER NOT NULL PRIMARY KEY,
CONSTRAINT non_negative_nbr CHECK (seq > 0),
CONSTRAINT numbers_are_complete CHECK ((SELECT COUNT(*) FROM Series) = (SELECT MAX(seq) FROM Series))
);
PRIMARY KEY 声明确保了唯一性。约束确保了积极性non_negative_nbr
。有了这两个约束,约束就确保了连续性numbers_are_complete
。
SQL Server 不支持检查约束中的子查询。当我尝试创建 Series 表时,收到如下错误:
Msg 1046, Level 15, State 1, Line 4
Subqueries are not allowed in this context. Only scalar expressions are allowed.
Msg 102, Level 15, State 1, Line 4
Incorrect syntax near ')'.
如果我删除 unsupported constraint numbers_are_complete
,我会得到这个定义:
CREATE TABLE Series (
seq INTEGER NOT NULL PRIMARY KEY,
CONSTRAINT non_negative_nbr CHECK (seq > 0)
);
当我尝试创建这个版本的系列时,它成功了:
Command(s) completed successfully.
这个版本的 Series 较弱,因为它不强制表中数字的连续性。
为了证明这一点,首先我必须填充表格。我采用了 Itzik Ben-Gan 在他的文章 ' Virtual Auxiliary Table of Numbers ' 中描述的一种技术,可以有效地处理 65,536 行:
WITH
N0(_) AS (SELECT NULL UNION ALL SELECT NULL),
N1(_) AS (SELECT NULL FROM N0 AS L CROSS JOIN N0 AS R),
N2(_) AS (SELECT NULL FROM N1 AS L CROSS JOIN N1 AS R),
N3(_) AS (SELECT NULL FROM N2 AS L CROSS JOIN N2 AS R),
N4(_) AS (SELECT NULL FROM N3 AS L CROSS JOIN N3 AS R)
INSERT INTO Series (
seq
)
SELECT
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS n
FROM N4;
查询产生如下输出:
(65536 row(s) affected)
现在我可以从这样的表中选择生成 65,536 行:
SELECT seq
FROM Series;
我已经截断了结果集,但它看起来像这样:
seq
1
2
...
65535
65536
自己检查一下,你会发现区间 [1, 65536] 中的每个数字都在结果集中。该系列是连续的。
但是我可以通过删除不是范围端点的任何行来打破连续性:
DELETE FROM Series
WHERE seq = 25788;
如果强制执行连续性,则此语句将引发错误,但它会成功:
(1 row(s) affected)
人类很难通过目视检查找到缺失值。他们必须首先怀疑某个值是否丢失,然后再去惹麻烦。由于这些原因,篡改 Series 数据是一种将细微错误引入依赖 Series 表连续的 SQL Server 应用程序的简单方法。
假设用户编写了一个查询,该查询从 Sequence 读取以枚举来自另一个源的行。在我篡改之后,该查询现在会在某个值附近产生不正确的结果 - 到第 25,788 行,一切都偏离了一个。
可以编写一个查询来检测 Series 表中的缺失值,但是如何约束该表以使缺失值是不可能的?