2016-05-12 56 views
4

这不是Check constraint on date的重复,但我可能错过了另一个类似的问题。Sql - Column Check Constraint based on Date

在MS SQL,您可以创建以下限制:

ALTER TABLE [X] WITH CHECK ADD CONSTRAINT [CCCHK03_TBX] CHECK 
(
    [TBX_YEAR] = DATEPART(year, GetDate()) 
) 

您可以插入记录得很好,但我无法虽然全面测试这意味着什么,会发生什么,当服务器日期剔到2017年?我的印象是,它将允许插入2017年,但它理论上将使2016年的所有记录无效。

此表是一个仅插入表,因此记录从不意味着可更新,所以这不是问题。我主要担心的是,是否可能导致由此导致的服务器稳定性问题?

我似乎无法找到与此相关的任何内容,但MS必须有一个允许这样的约束的理由。

通常我会建议创建一个插入触发器并检查,但这让我好奇。

编辑:从目的地数据的回答扩展测试:

IF (OBJECT_ID('tempdb..#CheckTest') IS NOT NULL) 
    DROP TABLE #CheckTest 
GO 

CREATE TABLE #CheckTest (MN INT) 
ALTER TABLE #CheckTest WITH CHECK ADD CONSTRAINT CHK_MN CHECK (MN = DATEPART(SECOND, GETDATE())) 
ALTER TABLE #CheckTest CHECK CONSTRAINT CHK_MN 
GO 

-- Control Test. This will fail with: 
--Msg 547, Level 16, State 0, Line 12 
--The INSERT statement conflicted with the CHECK constraint "CHK_MN". The conflict occurred in database "tempdb", table "dbo.#CheckTest", column 'MN'. 
--The statement has been terminated. 
INSERT INTO #CheckTest (MN) 
VALUES (DATEPART(SECOND, DATEADD(SECOND, 5, GETDATE()))) 

-- Add 5 different seconds. 
DECLARE @Counter int = 0; 

WHILE @Counter < 5 
BEGIN 
    INSERT INTO #CheckTest (MN) 
    VALUES (DATEPART(SECOND, GETDATE())) 

    SET @Counter += 1; 

    -- Delay for a second. 
    WAITFOR DELAY '00:00:01'; 
END 
GO 

-- Add a different second. 
-- Disabling and Enabling a check will work just fine so long as the check already exists. 
ALTER TABLE #CheckTest NOCHECK CONSTRAINT CHK_MN; 
INSERT INTO #CheckTest (MN) 
VALUES (DATEPART(SECOND, DATEADD(SECOND, 5, GETDATE()))) 
ALTER TABLE #CheckTest CHECK CONSTRAINT CHK_MN; 

-- Control Test. This will fail with: 
--Msg 547, Level 16, State 0, Line 12 
--The INSERT statement conflicted with the CHECK constraint "CHK_MN". The conflict occurred in database "tempdb", table "dbo.#CheckTest", column 'MN'. 
--The statement has been terminated. 
INSERT INTO #CheckTest (MN) 
VALUES (DATEPART(SECOND, DATEADD(SECOND, 5, GETDATE()))) 
GO 

-- Check table contents. 
SELECT * FROM #CheckTest; 
GO 

-- Dropping and recreating the check constraint will result in an error: 
--Msg 547, Level 16, State 0, Line 37 
--The ALTER TABLE statement conflicted with the CHECK constraint "CHK_MN". The conflict occurred in database "tempdb", table "dbo.#CheckTest", column 'MN'. 
ALTER TABLE #CheckTest DROP CONSTRAINT CHK_MN; 
ALTER TABLE #CheckTest WITH CHECK ADD CONSTRAINT CHK_MN CHECK (MN = DATEPART(SECOND, GETDATE())) 
GO 

DROP TABLE #CheckTest 
GO 

编辑2:基于测试和反馈,而这肯定是一个有趣的练习,肯定摘要

出现为100%完全有效,我肯定会建议完全从“未来证明”的观点来看,因为支票永远不会被改变。就我个人而言,我认为基于触发器的约束是最容易维护的。

+1

当约束失败时,不会有任何系统不稳定。约束条件是为了执行我们预定义的规则 – TheGameiswar

+3

现在我无法在文档中找到它,但最有可能的'CHECK'约束条件仅在行插入或更新(仅适用于插入或更新的行)。因此,您可以在今年插入2016年的行,并在明年插入2017年的行,但如果在2017年尝试更新2016年创建的行,则更新将失败。如果你不碰那个旧的行,它会保持原样。 –

+0

@VladimirBaranov这很好,谢谢你的加固,我也是这么看的。如果那是我可以标记的答案。 – Storm

回答

2

虽然我同意Vladimir Baranov的评论,我也想想以下内容。

查询优化器可以考虑CHECK限制,当数据被查询(example),但在documentation表示,这是受信任的约束

只是做查询优化器不考虑被定义 有约束NOCHECK。

当你创建约束指定WITH CHECK

ALTER TABLE [X] WITH CHECK ADD CONSTRAINT [CCCHK03_TBX] CHECK ... 

一年后(添加对应明年的某个行之后),你将不能够做到

ALTER TABLE [X] WITH CHECK CHECK CONSTRAINT [CCCHK03_TBX] 

争取做它会产生错误消息

邮件5 47,Level 16,State 0,Line ... ALTER TABLE语句 与CHECK约束“CCCHK03_TBX”冲突。冲突 发生在数据库“DbName”,表“dbo.X”,列“TBX_YEAR”中。

然而在sys.check_constraints系统视图

select is_not_trusted 
from sys.check_constraints 
where object_id = object_id('CCCHK03_TBX') 

你仍然会看到is_not_trusted = 0,那里的有效,我想,这就像is_not_trusted = 1(因为一些表中的行不再满足查询表达式) 。

我不确定,但我认为这可能会导致查询优化器将生成次优计划的情况,除非它足够聪明以至于不能在某些情况下考虑包含非确定性表达式的约束(我是无法找到这些信息,如果有人将这个信息说明的话,那就太棒了)。我相信查询优化器在这种情况下足够聪明,不会做出可能导致错误查询结果的错误判断。

+0

我真的很喜欢关于查询优化器的信息。关于启用CHECK,但只有在尝试添加支票时才会出现错误,而不是启用它。如果它已经添加,您可以根据问题中的更新测试安全地启用它。 – Storm

+1

@Storm,禁用约束用'改变表[T] NOCHECK约束[CK]'和重新启用回用'改变表[T]检查约束[CK]'将离开约束在'为is_not_trusted = 1'状态(这可以在'sys.check_constraints'中检查),尽管会启用约束,稍后的插入/更新将遵循约束规则。启用并不意味着可信。使其再次信任的唯一方法是使用检查检查约束[CK]'执行alter table [T]。但是最终这个约束是不可能的。 –

+0

您的评论现在有意义,在您的原始答案中并不十分清楚,也许只是更新您的答案,以更详细地阐述您在评论中发布的信息,因为信任问题是游戏规则改变者。 – Storm

3

好问题!

简短的回答是你会没事的。仅当在支票内插入或更新列时才检查支票。您现有的记录将不受年份变化的影响。

但您应该考虑一些潜在的问题。这种检查可以让你更难修改表格设计,尤其是通过SSMS(它倾向于创建一个新表格,从旧数据导入数据,删除旧数据并重命名新数据)。由于旧记录与当前约束规则不匹配,导入将失败。当然你仍然可以使用tSQL进行修改。

修宪表

-- Disable check. 
ALTER TABLE [Schema].[Table] NOCHECK CONSTRAINT [Check]; 

-- Makes changes here. 

-- Enable check. 
ALTER TABLE [Schema].[Table] CHECK CONSTRAINT [Check]; 

表内更新其他列时,你并不需要禁用检查。

测试查询

测试有点难以年,所以我换到几分钟。

CREATE TABLE #CheckTest 
    (
     MN INT CONSTRAINT CHK_MN CHECK (MN = DATEPART(MINUTE, GETDATE())) 
    ) 
; 

-- Add two different minutes. 
WHILE @COUNTER < 2 
BEGIN 

    INSERT INTO #CheckTest 
     (
      MN 
     ) 
    VALUES 
     (DATEPART(MINUTE, GETDATE())) 
    ; 

    SET @COUNTER = @COUNTER + 1; 

    -- Delay for a minute. 
    WAITFOR DELAY '00:01:00' 
END 

-- Check table contents. 
SELECT 
    * 
FROM 
    #CheckTest 
; 
+0

我真的很喜欢禁用和启用检查,我肯定会失败,但后来我意识到,试图创建检查时,只有输入。 – Storm

相关问题