DENGAN CHECK ADD CONSTRAINT diikuti oleh CHECK CONSTRAINT vs. ADD CONSTRAINT

133

Saya sedang melihat database sampel AdventureWorks untuk SQL Server 2008, dan saya melihat dalam skrip penciptaan mereka bahwa mereka cenderung menggunakan yang berikut:

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

segera diikuti oleh:

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

Saya melihat ini untuk kunci asing (seperti di sini), batasan unik dan CHECKbatasan reguler ; DEFAULTkendala menggunakan format biasa saya lebih akrab dengan seperti:

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

Apa bedanya, jika ada, antara melakukannya dengan cara pertama versus yang kedua?

Wayne Molina
sumber

Jawaban:

94

Sintaks pertama adalah redundan - PERIKSA DENGAN adalah default untuk kendala baru, dan kendala diaktifkan secara default juga.

Sintaks ini dihasilkan oleh studio manajemen SQL saat membuat skrip sql - Saya berasumsi itu semacam redundansi tambahan, mungkin untuk memastikan kendala diaktifkan bahkan jika perilaku kendala default untuk tabel diubah.

Chris Hynes
sumber
12
Sepertinya TANPA PERIKSA sebenarnya adalah default, itu hanya default untuk data baru. Dari msdn.microsoft.com/en-us/library/ms190273.aspx : "Jika tidak ditentukan, WITH CHECK diasumsikan untuk kendala baru, dan WITH NOCHECK diasumsikan untuk kendala yang diaktifkan kembali."
Zain Rizvi
8
@ZainRizvi: bukan data baru, batasan baru. Jika Anda menonaktifkan kendala dengan ALTER TABLE foo NOCHECK CONSTRAINT fk_bdan mengaktifkannya kembali dengan ALTER TABLE foo CHECK CONSTRAINT fk_bitu tidak memverifikasi kendala. ALTER TABLE foo WITH CHECK CHECK CONSTRAINT fk_bdiperlukan untuk memiliki data diverifikasi.
jmoreno
2
Tidak jelas bagi saya membaca ini pada awalnya. Baris kedua (redundan), adalah fungsi untuk menghidupkan kendala. Karena kendala diaktifkan secara default, baris kedua adalah redundan.
blindguy
47

Untuk menunjukkan bagaimana ini bekerja -

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;
Graeme
sumber
7
Bersihkan - DROP TABLE T2; DROP TABLE T1;
Graeme
8
Saya menambahkan kode pembersihan dari komentar Anda ke jawaban Anda yang sebenarnya untuk membantu copy-and-pasters fly-by-night di luar sana.
mwolfe02
18
"Fly-by-night copy-and-pasters" tampaknya agak negatif. Saya akan menganggap diri saya sebagai salah satu pengguna tumpukan (untuk kata-kata yang lebih positif ...) "yang menemukan jenis contoh terperinci ini sangat berharga".
GaTechThomas
2
Merendahkan atau tidak, 'terbang malam' terasa seperti menggambarkan saya dengan sempurna.
sanepete
21

Lebih lanjut ke komentar yang sangat baik di atas tentang kendala tepercaya:

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

Kendala yang tidak dipercaya, seperti namanya, tidak bisa dipercaya untuk secara akurat mewakili kondisi data dalam tabel saat ini. Namun, hal itu dapat dipercaya untuk memeriksa data yang ditambahkan dan dimodifikasi di masa mendatang.

Selain itu, kendala yang tidak dipercaya diabaikan oleh pengoptimal kueri.

Kode untuk mengaktifkan batasan pemeriksaan dan batasan kunci asing cukup buruk, dengan tiga arti kata "centang".

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".
Greenstone Walker
sumber
15

WITH NOCHECK digunakan juga ketika seseorang memiliki data yang ada dalam tabel yang tidak sesuai dengan batasan seperti yang ditentukan dan Anda tidak ingin itu bertabrakan dengan kendala baru yang Anda laksanakan ...

siang dan
sumber
13

WITH CHECK memang perilaku default namun praktik yang baik untuk dimasukkan dalam pengkodean Anda.

Perilaku alternatif tentu saja digunakan WITH NOCHECK, jadi ada baiknya untuk secara eksplisit mendefinisikan niat Anda. Ini sering digunakan ketika Anda bermain dengan / memodifikasi / mengganti partisi inline.

John Sansom
sumber
9

Batasan kunci asing dan pemeriksaan memiliki konsep dipercaya atau tidak dipercaya, serta diaktifkan dan dinonaktifkan. Lihat halaman MSDN untukALTER TABLE untuk detail lengkap.

WITH CHECK adalah standar untuk menambahkan kunci asing baru dan memeriksa batasan, WITH NOCHECK adalah default untuk mengaktifkan kembali kunci asing yang dinonaktifkan dan memeriksa kendala. Sangat penting untuk menyadari perbedaannya.

Karena itu, setiap pernyataan yang tampaknya berlebihan yang dihasilkan oleh utilitas hanya ada untuk keamanan dan / atau kemudahan pengkodean. Jangan khawatir tentang mereka.

Christian Hayter
sumber
Apakah tautan ini yang Anda maksud: msdn.microsoft.com/en-us/library/ms190273.aspx ? Apakah ini berarti bahwa kita harus melakukan tabel ALTER TABLE WITH CHECK CHECK CONSTRAINT ALL alih-alih melakukannya untuk setiap kendala?
Henrik Staun Poulsen
@HenrikStaunPoulsen: Ya itu tautannya. Tidak ada yang menghentikan Anda mengaktifkan setiap kendala secara individual, tetapi Anda harus mengatakan WITH CHECK CHECK CONSTRAINTuntuk membuatnya dipercaya.
Christian Hayter
Saya mencobanya; Saya menjalankan "ALTER TABLE [dfm]. [TRATransformError] DENGAN CHECK CHECK CONSTRAINT [FK_TRATransformError_ETLEvent]". Tetapi FK masih memiliki Is_Not_Trusted = 1. Kemudian saya menjatuhkan FK, dan menciptakannya kembali dengan "CHECK CHECK", dan sekarang saya memiliki Is_Not_Trusted = 0. Akhirnya. Apa kamu tahu kenapa? Harap dicatat bahwa saya selalu punya is_not_for_replication = 0
Henrik Staun Poulsen
@HenrikStaunPoulsen: Saya tidak tahu, itu selalu berhasil bagi saya. Saya menjalankan kueri yang ingin select * from sys.objects where [type] in ('C', 'F') and (objectproperty([object_id], 'CnstIsDisabled') = 1 or objectproperty([object_id], 'CnstIsNotTrusted') = 1)menemukan batasan yang dinonaktifkan dan tidak tepercaya. Setelah mengeluarkan pernyataan tabel perubahan yang sesuai seperti di atas, kendala-kendala itu hilang dari kueri, jadi saya bisa melihatnya berfungsi.
Christian Hayter
2
@HenrikStaunPoulsen, itu karena flag not_for_replication diatur ke 1. Ini menjadikan kendala sebagai tidak tepercaya. SELECT name, create_date, modified_date, is_disabled, is_not_for_replication, is_not_trusted FROM sys.foreign_keys WHERE is_not_trusted = 1 Dalam hal ini Anda perlu melepaskan dan membuat ulang batasan. Saya menggunakan ini untuk menyelesaikan bahwa gist.github.com/smoothdeveloper/ea48e43aead426248c0f Perlu diingat bahwa saat menghapus dan memperbarui tidak ditentukan dalam skrip ini dan Anda harus mempertimbangkannya.
kuklei
8

Berikut adalah beberapa kode yang saya tulis untuk membantu kami mengidentifikasi dan memperbaiki CONSTRAINT yang tidak terpercaya dalam DATABASE. Ini menghasilkan kode untuk memperbaiki setiap masalah.

    ;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;
Graeme
sumber