先检查加约束,然后再检查对比与增加约束


133

我正在查看SQL Server 2008的AdventureWorks示例数据库,并且在他们的创建脚本中看到他们倾向于使用以下内容:

ALTER TABLE [Production].[ProductCostHistory] WITH CHECK ADD 
CONSTRAINT [FK_ProductCostHistory_Product_ProductID] FOREIGN KEY([ProductID])
  REFERENCES [Production].[Product] ([ProductID])
GO

紧随其后的是:

ALTER TABLE [Production].[ProductCostHistory] CHECK CONSTRAINT     
[FK_ProductCostHistory_Product_ProductID]
GO

我在外键(如此处),唯一约束和常规CHECK约束中看到了这一点;DEFAULT约束使用我更熟悉的常规格式,例如:

ALTER TABLE [Production].[ProductCostHistory] ADD  CONSTRAINT  
[DF_ProductCostHistory_ModifiedDate]  DEFAULT (getdate()) FOR [ModifiedDate]
GO

第一种方法与第二种方法有什么区别(如果有)?

Answers:


94

第一种语法是多余的-WITH CHECK是新约束的默认设置,并且该约束在默认情况下也是打开的。

这种语法是由SQL Management Studio在生成sql脚本时生成的-我假设它具有某种额外的冗余性,以确保即使更改了表的默认约束行为,也可以确保启用了约束。


12
看起来WITH CHECK实际上并不是默认值,它只是新数据的默认值。来自msdn.microsoft.com/zh-cn/library/ms190273.aspx:“如果未指定,则将WITH CHECK用作新约束,而将WITH NOCHECK用作重新启用的约束。”
Zain Rizvi 2013年

8
@ZainRizvi:不是新数据,新约束。如果您使用禁用了约束,ALTER TABLE foo NOCHECK CONSTRAINT fk_b然后又重新启用了约束,则ALTER TABLE foo CHECK CONSTRAINT fk_b不会验证约束。 ALTER TABLE foo WITH CHECK CHECK CONSTRAINT fk_b为了验证数据是必需的。
jmoreno'3

2
最初阅读此书对我来说还不清楚。第二行(冗余)是打开约束的功能。由于约束默认情况下处于启用状态,因此第二行是多余的。
blindguy

47

为了演示其工作原理-

CREATE TABLE T1 (ID INT NOT NULL, SomeVal CHAR(1));
ALTER TABLE T1 ADD CONSTRAINT [PK_ID] PRIMARY KEY CLUSTERED (ID);

CREATE TABLE T2 (FKID INT, SomeOtherVal CHAR(2));

INSERT T1 (ID, SomeVal) SELECT 1, 'A';
INSERT T1 (ID, SomeVal) SELECT 2, 'B';

INSERT T2 (FKID, SomeOtherVal) SELECT 1, 'A1';
INSERT T2 (FKID, SomeOtherVal) SELECT 1, 'A2';
INSERT T2 (FKID, SomeOtherVal) SELECT 2, 'B1';
INSERT T2 (FKID, SomeOtherVal) SELECT 2, 'B2';
INSERT T2 (FKID, SomeOtherVal) SELECT 3, 'C1';  --orphan
INSERT T2 (FKID, SomeOtherVal) SELECT 3, 'C2';  --orphan

--Add the FK CONSTRAINT will fail because of existing orphaned records
ALTER TABLE T2 ADD CONSTRAINT FK_T2_T1 FOREIGN KEY (FKID) REFERENCES T1 (ID);   --fails

--Same as ADD above, but explicitly states the intent to CHECK the FK values before creating the CONSTRAINT
ALTER TABLE T2 WITH CHECK ADD CONSTRAINT FK_T2_T1 FOREIGN KEY (FKID) REFERENCES T1 (ID);    --fails

--Add the CONSTRAINT without checking existing values
ALTER TABLE T2 WITH NOCHECK ADD CONSTRAINT FK_T2_T1 FOREIGN KEY (FKID) REFERENCES T1 (ID);  --succeeds
ALTER TABLE T2 CHECK CONSTRAINT FK_T2_T1;   --succeeds since the CONSTRAINT is attributed as NOCHECK

--Attempt to enable CONSTRAINT fails due to orphans
ALTER TABLE T2 WITH CHECK CHECK CONSTRAINT FK_T2_T1;    --fails

--Remove orphans
DELETE FROM T2 WHERE FKID NOT IN (SELECT ID FROM T1);

--Enabling the CONSTRAINT succeeds
ALTER TABLE T2 WITH CHECK CHECK CONSTRAINT FK_T2_T1;    --succeeds; orphans removed

--Clean up
DROP TABLE T2;
DROP TABLE T1;

7
清理DROP TABLE T2; DROP TABLE T1;
Graeme

8
我将您评论中的清理代码添加到了您的实际答案中,以帮助您进行夜间的复制粘贴。
mwolfe02 2014年

18
“夜间飞行复制粘贴”似乎有点负面。我会认为自己是那些使用堆栈的用户(为了获得更积极的措辞……)“发现这些类型的详细示例非常有价值”。
GaTechThomas

2
不管是否贬义,“夜间飞行”感觉都完美地描述了我。
sanepete

21

除了上述有关受信任约束的出色评论之外:

select * from sys.foreign_keys where is_not_trusted = 1 ;
select * from sys.check_constraints where is_not_trusted = 1 ;

顾名思义,一个不受信任的约束现在不能被信任以准确表示表中数据的状态。但是,它可以但将来可以用来检查添加和修改的数据。

此外,查询优化程序会忽略不受信任的约束。

用于启用检查约束和外键约束的代码非常糟糕,具有“检查”一词的三个含义。

ALTER TABLE [Production].[ProductCostHistory] 
WITH CHECK -- This means "Check the existing data in the table".
CHECK CONSTRAINT -- This means "enable the check or foreign key constraint".
[FK_ProductCostHistory_Product_ProductID] -- The name of the check or foreign key constraint, or "ALL".

15

WITH NOCHECK 当一个表中的现有数据不符合所定义的约束并且您不希望它与正在实现的新约束冲突时,也会使用。


13

WITH CHECK 确实是默认行为,但是在您的编码中包含它是一种好习惯。

当然可以使用替代行为WITH NOCHECK,因此最好明确定义您的意图。当您在使用/修改/切换嵌入式分区时,通常会使用它。


9

外键和检查约束具有被信任或不受信任以及被启用和禁用的概念。请参阅MSDN页面以获取ALTER TABLE完整的详细信息,。

WITH CHECK 是添加新外键和检查约束的默认设置, WITH NOCHECK的默认值是重新启用禁用的外键和检查约束的默认值。意识到差异很重要。

话虽如此,实用程序生成的任何显然多余的语句只是为了安全和/或简化编码而存在。不用担心


您是引用此链接吗:msdn.microsoft.com/en-us/library/ms190273.aspx?这是否意味着我们必须使用CHECK CHECK CONSTRAINT ALL做一个ALTER TABLE表,而不是对每个约束都这样做?
Henrik Staun Poulsen

@HenrikStaunPoulsen:是的,这就是链接。没有什么可以阻止您单独启用每个约束的,但是您必须要说服WITH CHECK CHECK CONSTRAINT它们获得信任。
克里斯蒂安·海特

我试过了 我运行了“ ALTER TABLE [dfm]。[TR​​ATransformError]有检查检查约束[FK_TRATransformError_ETLEvent]”。但是FK仍然具有Is_Not_Trusted = 1。然后,我放下FK,并使用“ WITH CHECK CHECK”重新创建它,现在我的Is_Not_Trusted = 0。最后。你知道为什么吗?请注意,我一直都是is_not_for_replication = 0
Henrik Staun Poulsen

@HenrikStaunPoulsen:我不知道,它对我来说一直很好。我运行查询select * from sys.objects where [type] in ('C', 'F') and (objectproperty([object_id], 'CnstIsDisabled') = 1 or objectproperty([object_id], 'CnstIsNotTrusted') = 1)来查找禁用和不受信任的约束。在发出上述适当的alter table语句之后,那些约束确实从查询中消失了,因此我可以看到它正在工作。
克里斯蒂安·海特

2
@HenrikStaunPoulsen,这是因为not_for_replication标志设置为1。这使得约束不受信任。SELECT名称,create_date,modify_date,is_disabled,is_not_for_replication,is_not_trusted FROM sys.foreign_keys,其中is_not_trusted = 1在这种情况下,您需要删除并重新创建约束。我用它来完成gist.github.com/smoothdeveloper/ea48e43aead426248c0f请记住,此脚本中未指定删除和更新,因此您需要考虑到这一点。
kuklei

8

这是我编写的一些代码,以帮助我们识别和更正数据库中不受信任的约束。它生成用于修复每个问题的代码。

    ;WITH Untrusted (ConstraintType, ConstraintName, ConstraintTable, ParentTable, IsDisabled, IsNotForReplication, IsNotTrusted, RowIndex) AS
(
    SELECT 
        'Untrusted FOREIGN KEY' AS FKType
        , fk.name AS FKName
        , OBJECT_NAME( fk.parent_object_id) AS FKTableName
        , OBJECT_NAME( fk.referenced_object_id) AS PKTableName 
        , fk.is_disabled
        , fk.is_not_for_replication
        , fk.is_not_trusted
        , ROW_NUMBER() OVER (ORDER BY OBJECT_NAME( fk.parent_object_id), OBJECT_NAME( fk.referenced_object_id), fk.name) AS RowIndex
    FROM 
        sys.foreign_keys fk 
    WHERE 
        is_ms_shipped = 0 
        AND fk.is_not_trusted = 1       

    UNION ALL

    SELECT 
        'Untrusted CHECK' AS KType
        , cc.name AS CKName
        , OBJECT_NAME( cc.parent_object_id) AS CKTableName
        , NULL AS ParentTable
        , cc.is_disabled
        , cc.is_not_for_replication
        , cc.is_not_trusted
        , ROW_NUMBER() OVER (ORDER BY OBJECT_NAME( cc.parent_object_id), cc.name) AS RowIndex
    FROM 
        sys.check_constraints cc 
    WHERE 
        cc.is_ms_shipped = 0
        AND cc.is_not_trusted = 1

)
SELECT 
    u.ConstraintType
    , u.ConstraintName
    , u.ConstraintTable
    , u.ParentTable
    , u.IsDisabled
    , u.IsNotForReplication
    , u.IsNotTrusted
    , u.RowIndex
    , 'RAISERROR( ''Now CHECKing {%i of %i)--> %s ON TABLE %s'', 0, 1' 
        + ', ' + CAST( u.RowIndex AS VARCHAR(64))
        + ', ' + CAST( x.CommandCount AS VARCHAR(64))
        + ', ' + '''' + QUOTENAME( u.ConstraintName) + '''' 
        + ', ' + '''' + QUOTENAME( u.ConstraintTable) + '''' 
        + ') WITH NOWAIT;'
    + 'ALTER TABLE ' + QUOTENAME( u.ConstraintTable) + ' WITH CHECK CHECK CONSTRAINT ' + QUOTENAME( u.ConstraintName) + ';' AS FIX_SQL
FROM Untrusted u
CROSS APPLY (SELECT COUNT(*) AS CommandCount FROM Untrusted WHERE ConstraintType = u.ConstraintType) x
ORDER BY ConstraintType, ConstraintTable, ParentTable;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.