【问题标题】:Table Check Constraint allows invalid data表检查约束允许无效数据
【发布时间】:2014-04-10 20:53:39
【问题描述】:

当我使用下面的测试脚本创建检查约束时,违反约束的数据仍然允许进入表,并且约束仍然显示为受信任。

我意识到检查约束不能正确检查 NULL(它包括 column = null 而不是 column IS null),但我仍然希望 SQL Server 不会允许 'ASDF'、'3' 或 NULL值,因为检查条件对这些值的评估结果为假。有人可以解释为什么这个检查约束允许以下值:NULL、'3'、'ASDF'?

如果我将约束条件更改为(checkMe is null or checkMe = '1' or checkMe = '2'),那么它会按预期工作。

SQL Server 版本:Microsoft SQL Server 2008 R2 (SP2) - 10.50.4000.0 (X64)

CREATE TABLE dbo.testCheck(checkMe varchar(50));
go

向表中插入数据

INSERT INTO dbo.testCheck(checkMe)
VALUES ('1'),('2'),(NULL),('3');
GO

添加约束,检查以便检查现有数据。我希望 NULL 和 '3' 都违反了这个检查,但它以某种方式成功了。

ALTER TABLE dbo.testCheck WITH CHECK 
ADD CONSTRAINT ck_testCheck 
CHECK (checkMe = null or checkMe = '1' or checkMe = '2');
GO

添加检查约束后尝试插入无效数据...成功了吗?

INSERT INTO dbo.testCheck(checkMe) VALUES('ASDF');
GO

显示该表包含无效数据,并且该约束被标记为受信任,这意味着该表中的所有数据都已针对该约束进行了验证

SELECT *
    --this is the same logic as in the check constraint, shows 3 rows that do not pass
    , checkConstraintLogic = case when (checkMe = null or checkMe = '1' or checkMe = '2') then 'PASS' else 'FAIL' end
FROM dbo.testCheck;
go

SELECT parentObject = isnull(OBJECT_SCHEMA_NAME(k.parent_object_id) + '.', '') + OBJECT_NAME(k.parent_object_id)
    , k.name, k.is_not_trusted
FROM sys.check_constraints k 
WHERE k.parent_object_id = object_id('dbo.testCheck')
ORDER BY 1;
GO

脚本输出:

【问题讨论】:

  • 如果任何人都更容易查看,这里还有一个 SQL Fiddle:sqlfiddle.com/#!3/69dcc/2
  • 您不能使用=NULL 进行比较。你必须使用IS NULL
  • @a_horse_with_no_name 您可以使用= 与 SQL Server 中的 NULL 进行比较,它只会返回 Unknown 作为结果。我了解空值在普通 T-SQL 中是如何工作的。这是很久以前在应用程序中错误设置的约束,我只是不知道为什么在这种情况下 CHECK 约束的计算结果与普通 SQL 语句不同。通常 (Unknown OR False OR False) 在 SQL Server 条件下计算结果为 False,但在这种情况下使用 Check 约束它计算结果为 True。

标签: sql sql-server sql-server-2008-r2 check-constraints check-constraint


【解决方案1】:

检查约束与 WHERE 子句的不同之处在于,如果表达式计算结果为空标记,则 CHECK 允许修改。澄清一下:WHERE 子句过滤掉表达式计算结果为 falsenull 标记 的行;检查约束仅过滤掉评估为 false 的修改。

您编写的表达式总是计算为 null,因为它内部有一个与 null 的比较。将= null 更改为is null

此外,Check 约束的不同用法对 CHECK/NOCHECK 有不同的默认值,因此您应该养成始终指定它的习惯。

试试下面的。

ALTER TABLE dbo.testCheck WITH CHECK 
WITH CHECK
ADD CONSTRAINT ck_testCheck 
CHECK (checkMe IS null or checkMe = '1' or checkMe = '2');

编辑:关于“但我想知道为什么这个示例的计算结果为 True”,正如您所计算的那样,您的示例计算结果不是 true 而是一个 null 标记 em>,这是 Check 约束所允许的。对不起,我应该解释得更好一点。

【讨论】:

  • 我了解正确的用法应该是什么(并在 OP 中说明),但我想知道 为什么 此示例的计算结果为 True。说值 '3',这将评估:('3' = null OR '3' = '1' OR '3' = '2'),这将是:(NULL/Unknown OR False OR False),这将最终成为未知。所以我猜 Check 约束的工作方式与其他 T-SQL 处理 NULL 的方式不同,并将 Null/Unknown 结果视为有效?
  • 啊,我刚刚在 BOL 中确认了您的回答,其中指出:“当检查的条件对于表中的任何行都不是 FALSE 时,CHECK 约束返回 TRUE”。谢谢!
【解决方案2】:

如指定here:

CHECK 约束拒绝评估为 FALSE 的值。

换句话说:

当检查的条件对于表中的任何行都不是 FALSE 时,CHECK 约束返回 TRUE。

在您的情况下,表达式 (checkMe is null or checkMe = '1' or checkMe = '2') 的计算结果为 NULL(又名 Unknown),并且 CHECK 约束返回 TRUE(因为它检查的条件不是 FALSE)

来自同一来源的一个很好的例子:

例如,假设您在一个 int 列 MyColumn 上放置一个约束 指定 MyColumn 只能包含值 10 (MyColumn = 10)。如果将值 NULL 插入 MyColumn,数据库引擎 插入 NULL 并且不返回错误。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-03
    • 2018-04-13
    • 1970-01-01
    • 1970-01-01
    • 2013-05-04
    • 2016-02-22
    相关资源
    最近更新 更多