Sql server 为什么我的检查约束不能停止这个空插入?
有人能解释为什么SQL Server允许下面代码中的第三个插入(标记为查询数据)吗 据我所知,检查约束只允许:Sql server 为什么我的检查约束不能停止这个空插入?,sql-server,sql-server-2008-r2,Sql Server,Sql Server 2008 R2,有人能解释为什么SQL Server允许下面代码中的第三个插入(标记为查询数据)吗 据我所知,检查约束只允许: code为空,System为空 code不为空,System为1 我的第一个想法是ANSI NULLS,但将它们设置为打开或关闭没有任何区别 这是我们在应用程序中发现的一个更大问题的简化示例(系统根据(1、2等)中的数字列表进行了检查)。我们用一个外键(而不是中的)和一个新的检查约束替换了该检查,该约束允许null或notnull;这样做防止了第三次插入 IF EXISTS (S
为空,code
为空System
不为空,code
为System
1
ANSI NULLS
,但将它们设置为打开
或关闭
没有任何区别
这是我们在应用程序中发现的一个更大问题的简化示例(系统根据(1、2等)中的数字列表进行了检查)。我们用一个外键(而不是中的)和一个新的检查约束替换了该检查,该约束允许null或notnull;这样做防止了第三次插入
IF EXISTS (SELECT * FROM sys.check_constraints WHERE object_id = OBJECT_ID(N'[dbo].[CK_TestCheck]') AND parent_object_id = OBJECT_ID(N'[dbo].[TestCheck]'))
ALTER TABLE [dbo].[TestCheck] DROP CONSTRAINT [CK_TestCheck]
GO
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[TestCheck]') AND type in (N'U'))
DROP TABLE [dbo].[TestCheck]
GO
SET ANSI_NULLS ON
GO
CREATE TABLE TestCheck(
[Id] [int] IDENTITY(1,1) NOT NULL,
[Code] [varchar](50) NULL,
[System] [tinyint] NULL,
PRIMARY KEY CLUSTERED ([Id] ASC))
GO
ALTER TABLE [dbo].[TestCheck] WITH CHECK ADD CONSTRAINT [CK_TestCheck] CHECK
(
([Code] IS NULL AND [System] IS NULL) --Both null
OR
([Code] IS NOT NULL AND [System] = 1) --Both not null ????
)
GO
ALTER TABLE [dbo].[TestCheck] CHECK CONSTRAINT [CK_TestCheck]
GO
--Good Data
insert TestCheck (Code, [System]) Values(null, null);
insert TestCheck (Code, [System]) Values('123', 1);
--Query Data
insert TestCheck (Code, [System]) Values('123', null);
--Bad data stopped
insert TestCheck (Code, [System]) Values(null, 1);
insert TestCheck (Code, [System]) Values('123', 4);
select * from TestCheck
Where
case when
(
([Code] IS NULL AND [System] IS NULL) --Both null
OR
([Code] IS NOT NULL AND [System] in (1, 2, 3)) --Both not null ????
)
then 0 else 1 end
= 1
对值123,NULL
的当前约束求值的结果未定义
([code]为空,[System]为空)
计算结果为False
([code]不为空,并且(1,2,3)中的[System]计算结果为未定义
结果是未定义
检查约束拒绝计算为FALSE的值。因为空
值计算为未知值,表达式中的值可能会覆盖
约束
您应该将(1,2,3)
中对[System]的检查更改为(1,2,3)
中的ISNULL([System],0)
您的检查约束将变为
ALTER TABLE [dbo].[TestCheck] WITH CHECK ADD CONSTRAINT [CK_TestCheck] CHECK
(
([Code] IS NULL AND [System] IS NULL) --Both null
OR
([Code] IS NOT NULL AND ISNULL([System], 0) IN (1, 2, 3)) --Both not null ????
)
欢迎使用SQL奇妙的三值逻辑。您可能知道,也可能不知道,与null
进行任何标准比较的结果都不是TRUE
,或FALSE
,而是未知
在WHERE
子句中,整个子句的计算结果必须为TRUE
在检查
约束中,整个约束的计算结果必须不是FALSE
因此,我们有:
([Code] IS NULL AND [System] IS NULL) --Both null
OR
([Code] IS NOT NULL AND [System] = 1) --Both not null ????
它变成(对于查询数据):
而任何一侧带有UNKNOWN
的运算符或另一侧的运算符计算结果为UNKNOWN
,因此总体结果为UNKNOWN
。这不是FALSE,因此评估check约束是成功的
如果您希望System
不为空,那么我最清楚的是,如果您将其添加为一个额外的明确要求
([Code] IS NULL AND [System] IS NULL) --Both null
OR
([Code] IS NOT NULL AND [System] IS NOT NULL AND [System] = 1) --Both not null ????
它的定义方式可能有点奇怪,但它与其他约束的工作方式是一致的,例如,外键约束可能有可为空的列,如果这些列中有任何一列为空,则引用表中不必有匹配的行。谢谢,我的印象是,设置ANSI_NULL是控制这种行为的一种方法?@DaveShaw-我认为您需要将其设置为关闭
,这可能有另一面effects@DaveShaw-这是ANSI行为,您已在
(正如您应该做的那样-使用SQL Server并关闭ANSI_NULLS
确实不是一个好主意,因为总有一天会这样做)对于当前的情况来说,这并不重要,但该声明是不正确的,“并且任何一边未知或另一边未知的运算符都会计算为未知”。如果运算符为和,则为正确,但如果运算符为或,则您有例如“true或unknown”然后它的计算结果为真,这也是非常合乎逻辑的,因为使用OR,如果您不知道其中一个值,只要其中一个值为真,这并不重要。我甚至必须在这里更正我自己,该声明在所有情况下都不正确。如果您有“false AND unknown”它的计算结果不是未知的,它的计算结果是false,这也是合乎逻辑的,因为如果一个值使用AND运算符为false,那么不管另一个值是什么,它都将计算为false在未定义后的括号中。这绝对不是错的。@Damien_不信者-我知道你的意思,但我已将它添加到括号中,说明未定义
对最终结果的作用。我将在答案中添加此注释。但你添加的内容不是真的。如果检查约束的最终结果是未知
,则不是创建的结果与评估为真的结果一样
-这让OP感到惊讶。@Damien_the_unsiver-你是对的offcourse@Damien_The_Unbeliever-我已经改变了我的答案,并对你的答案投了赞成票,因为你第一次解释的正确和更好。
([Code] IS NULL AND [System] IS NULL) --Both null
OR
([Code] IS NOT NULL AND [System] IS NOT NULL AND [System] = 1) --Both not null ????