2009-02-09 148 views
106

我期待在爲SQL Server 2008 AdventureWorks示例數據庫,我在他們的創建腳本,看到他們往往會使用以下命令:WITH CHECK ADD CONSTRAINT之後CHECK約束與ADD CONSTRAINT

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 

的區別是什麼,如果有的話,做的第一方式與第二間?

回答

72

第一種語法是多餘的 - WITH CHECK是新約束的缺省值,約束在默認情況下也是打開的。

該語法是由SQL管理工作室在生成sql腳本時生成的 - 我假設它是某種額外的冗餘,可能確保即使對錶的默認約束行爲更改也啓用約束。

+9

它看起來不像WITH CHECK實際上是默認值,它只是新數據的默認值。從http://msdn.microsoft.com/en-us/library/ms190273.aspx:「如果未指定,則假定WITH CHECK用於新的約束,並且假定WITH NOCHECK用於重新啓用的約束。」 – 2013-12-31 21:00:23

+3

@ 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 2015-03-14 01:16:05

+1

我最初並不清楚這一點。第二條(冗餘)行是打開約束的函數。由於約束默認開啓,所以第二行是多餘的。 – blindguy 2016-02-26 21:20:37

12

WITH CHECK確實是默認行爲,但在您的編碼中包含這是一種很好的做法。

另一種行爲當然是使用WITH NOCHECK,所以明確定義你的意圖是很好的。當您正在使用/修改/切換內聯分區時,通常會使用此選項。

9

外鍵和檢查約束具有受信任或不受信任的概念,以及被啓用和禁用。有關完整的詳細信息,請參閱MSDN頁面ALTER TABLE

WITH CHECK是添加新外鍵和檢查約束的默認值,WITH NOCHECK是重新啓用禁用外鍵和檢查約束的默認值。瞭解其中的差異很重要。儘管如此,公用事業生成的任何明顯多餘的陳述僅僅是爲了安全和/或易於編碼。不要擔心他們。

13

WITH NOCHECK也可用於當表中存在不符合約束的表中的現有數據,並且您不希望它與您正在實施的新約束相沖突時...

37

爲了演示works--

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; 
19

除了上述優秀評論有關受信任的約束:

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". 
5

下面是一些代碼,我寫來幫助我們確定並在數據庫中正確不可信的約束。它生成代碼來解決每個問題。

;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; 
相關問題