WITH CHECK ADD CONSTRAINTの後にCHECK CONSTRAINTとADD CONSTRAINTが続く


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

最初の方法と2番目の方法で違いがあるとすれば、それは何ですか?

回答:


94

最初の構文は冗長です。WITHCHECKは新しい制約のデフォルトであり、制約もデフォルトでオンになっています。

この構文は、SQLスクリプトを生成するときにSQL管理スタジオによって生成されます-おそらく、テーブルのデフォルトの制約動作が変更された場合でも制約が確実に有効になるように、何らかの余分な冗長性があると思います。


12
WITH CHECKが実際にデフォルトであるようには見えません。これは新しいデータのデフォルトにすぎません。msdn.microsoft.com/en-us/library/ms190273.aspxから:「指定しない場合、WITH CHECKは新しい制約に対して想定され、WITH NOCHECKは再度有効にされた制約に対して想定されます。」
Zain Rizvi

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 2015年

2
これを最初に読んだのははっきりしませんでした。2番目の(冗長な)行は、制約をオンにする関数です。制約はデフォルトでオンになっているため、2行目は冗長です。
blindguy 2016

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
「Fly-by-night copy-and-pasters」は少し否定的です。私は自分をこれらのスタックユーザーの1人(よりポジティブな言い回しのために...)と見なします。
GaTechThomas 2015年

2
軽蔑的であろうとなかろうと、「夜の飛行」はそれが私を完全に説明しているように感じます。
sanepete

21

上記の信頼できる制約に関する優れたコメントに加えて、

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

信頼できない制約は、その名前が示すように、現時点ではテーブル内のデータの状態を正確に表すために信頼することはできません。ただし、将来的に追加および変更されるデータを確認することはできます。

さらに、信頼できない制約はクエリオプティマイザーによって無視されます。

チェック制約と外部キー制約を有効にするコードはかなり悪く、「チェック」という単語の3つの意味があります。

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ですか?これは、制約ごとに行うのではなく、ALTER TABLEテーブルをCHECK CHECK CONSTRAINT ALLで実行する必要があることを意味しますか?
Henrik Staun Poulsen 2015

@HenrikStaunPoulsen:はい、それはリンクです。各制約を個別に有効にすることを妨げるものは何もありませんが、WITH CHECK CHECK CONSTRAINTそれらを信頼できるようにする必要があります。
クリスチャンヘイター、2015

私はそれを試しました。「ALTER TABLE [dfm]。[TR​​ATransformError] WITH CHECK CHECK CONSTRAINT [FK_TRATransformError_ETLEvent]」を実行しました。ただし、FKにはIs_Not_Trusted = 1がまだあります。次に、FKを削除し、「WITH CHECK CHECK」で再作成しました。これで、Is_Not_Trusted = 0が得られました。やっと。なぜなのかご存知ですか?私はいつもis_not_for_replication = 0持っていたことに注意してください
ヘンリックStaunポウルセン

@HenrikStaunPoulsen:よくわかりませんが、常に問題なく機能しています。select * from sys.objects where [type] in ('C', 'F') and (objectproperty([object_id], 'CnstIsDisabled') = 1 or objectproperty([object_id], 'CnstIsNotTrusted') = 1)無効で信頼できない制約を見つけるようなクエリを実行します。上記の適切なalter tableステートメントを発行した後、これらの制約はクエリから消えるため、機能していることがわかります。
Christian Hayter、2015

2
@ HenrikStaunPoulsen、not_for_replicationフラグが1に設定されているためです。これにより、制約が信頼できないものになります。SELECT name、create_date、modify_date、is_disabled、is_not_for_replication、is_not_trusted FROM sys.foreign_keys WHERE is_not_trusted = 1この場合、制約を削除して再作成する必要があります。これを使用して、gist.github.com / smoothdeveloper / ea48e43aead426248c0fを実行します。削除時と更新時はこのスクリプトで指定されておらず、それを考慮する必要があることに注意してください。
kuklei 2017

8

以下は、データベース内の信頼できないCONSTRAINTを特定して修正するために私が作成したコードです。各問題を修正するコードを生成します。

    ;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;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.