ÇEK EKLE KISITLAMASI İLE ve ardından KONTROL KISITLAMASI - KISIT EKLE


135

SQL Server 2008 için AdventureWorks örnek veritabanına bakıyorum ve oluşturma komut dosyalarında aşağıdakileri kullanma eğiliminde olduklarını görüyorum:

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

hemen ardından:

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

Bunu yabancı anahtarlar (burada olduğu gibi), benzersiz kısıtlamalar ve düzenli CHECKkısıtlamalar için görüyorum ; DEFAULTkısıtlamalar daha aşina olduğum normal biçimi kullanır, örneğin:

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

Eğer varsa, bunu birinci yoldan yapmakla ikinciyi yapmak arasındaki fark nedir?

Yanıtlar:


96

İlk sözdizimi gereksizdir - WITH CHECK, yeni kısıtlamalar için varsayılandır ve kısıtlama da varsayılan olarak açıktır.

Bu sözdizimi, sql betikleri oluştururken SQL yönetim stüdyosu tarafından üretilir - bunun bir tür fazladan fazlalık olduğunu varsayıyorum, muhtemelen bir tablo için varsayılan kısıtlama davranışı değiştirilse bile kısıtlamanın etkinleştirilmesini sağlamak için.


12
İLE CHECK aslında varsayılan gibi görünmüyor, yalnızca yeni veriler için varsayılandır. Gönderen msdn.microsoft.com/en-us/library/ms190273.aspx : "FİYATLARI yeni kısıtlamaları nedeniyle varsayılır ve İLE NOCHECK yeniden etkin kısıtlamaları nedeniyle varsayılır İLE belirtilen etmiyorsa."
Zain Rizvi

8
@ZainRizvi: yeni veri değil, yeni kısıtlamalar. Bir kısıtlamayı ile devre dışı bırakır ALTER TABLE foo NOCHECK CONSTRAINT fk_bve ardından onunla yeniden etkinleştirirseniz ALTER TABLE foo CHECK CONSTRAINT fk_b, kısıtlamayı doğrulamaz. ALTER TABLE foo WITH CHECK CHECK CONSTRAINT fk_bverilerin doğrulanması için gereklidir.
jmoreno

2
Bunu başlangıçta okuduğum açık değildi. İkinci (fazlalık) satır, kısıtlamayı etkinleştirme işlevidir. Kısıtlama varsayılan olarak açık olduğundan, ikinci satır gereksizdir.
blindguy

47

Bunun nasıl çalıştığını göstermek için--

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
Temizle-- DROP TABLE T2; DROP TABLE T1;
Graeme

8
Kopyalama ve yapıştırma işlemlerine yardımcı olmak için yorumunuzdaki temizleme kodunu gerçek cevabınıza ekledim.
mwolfe02

18
"Geceleyin kopyalama ve yapıştırma" biraz olumsuz görünüyor. Kendimi, "bu tür ayrıntılı örnekleri son derece değerli bulan" yığın kullanıcılarından biri olarak görüyorum (daha olumlu ifadeler için ...).
GaTechThomas

2
Aşağılayıcı ya da değil, 'geceden uçmak' beni mükemmel bir şekilde tanımlıyor gibi geliyor.
sanepete

21

Güvenilir kısıtlamalarla ilgili yukarıdaki mükemmel yorumlara ek olarak:

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

Güvenilmeyen bir kısıtlama, adından da anlaşılacağı gibi, şu anda tablodaki verilerin durumunu doğru bir şekilde temsil etmek için güvenilemez. Ancak olabilir, ancak gelecekte eklenen ve değiştirilen verileri kontrol etmek için güvenilir olabilir.

Ayrıca, güvenilmeyen kısıtlamalar sorgu iyileştirici tarafından dikkate alınmaz.

Kontrol kısıtlamalarını ve yabancı anahtar kısıtlamalarını etkinleştirecek kod, "kontrol" kelimesinin üç anlamı ile oldukça kötüdür.

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 Bir tabloda tanımlandığı gibi kısıtlamaya uymayan mevcut veriler olduğunda ve uyguladığınız yeni kısıtlamaya ters düşmesini istemediğinizde de kullanılır ...


13

WITH CHECK aslında varsayılan davranıştır, ancak kodlamanıza dahil etmeniz iyi bir uygulamadır.

Alternatif davranış elbette kullanmaktır WITH NOCHECK, bu nedenle niyetlerinizi açıkça tanımlamak iyidir. Bu genellikle satır içi bölümlerle oynarken / değiştirirken / değiştirirken kullanılır.


9

Yabancı anahtar ve kontrol kısıtlamaları, etkin veya devre dışı olmanın yanı sıra güvenilir veya güvenilmez olma konseptine sahiptir. Tüm ayrıntılar için MSDN sayfasına ALTER TABLEbakın.

WITH CHECKyeni yabancı anahtar eklemek ve kısıtlamaları kontrol etmek WITH NOCHECKiçin varsayılandır, devre dışı bırakılan yabancı anahtarı yeniden etkinleştirmek ve kısıtlamaları kontrol etmek için varsayılandır. Farkın farkında olmak önemlidir.

Bununla birlikte, yardımcı programlar tarafından üretilen görünürde fazlalık olan tüm ifadeler, güvenlik ve / veya kodlama kolaylığı için oradadır. Onlar için endişelenme.


Bu başvurduğunuz bağlantı mı: msdn.microsoft.com/en-us/library/ms190273.aspx ? Bu, her kısıtlama için bunu yapmak yerine CHECK CHECK CONSTRAINT ALL İLE ALTER TABLE tablosu yapmamız gerektiği anlamına mı geliyor?
Henrik Staun Poulsen

@HenrikStaunPoulsen: Evet, bağlantı bu. Her kısıtlamayı ayrı ayrı etkinleştirmenizi engelleyen hiçbir şey yoktur, ancak WITH CHECK CHECK CONSTRAINTonlara güvenmek için söylemeniz gerekir.
Christian Hayter

Bunu denedim; CHECK CHECK CONSTRAINT [FK_TRATransformError_ETLEvent] İLE "ALTER TABLE [dfm]. [TRATransformError]’ ı çalıştırdım. Ancak FK hala Is_Not_Trusted = 1'e sahiptir. Sonra FK'yi bıraktım ve "WITH CHECK CHECK" ile yeniden yarattım ve şimdi Is_Not_Trusted = 0 var. Sonunda. Neden biliyor musun? Lütfen her zaman is_not_for_replication = 0
Henrik Staun

@HenrikStaunPoulsen: Bilmiyorum, benim için her zaman iyi çalıştı. select * from sys.objects where [type] in ('C', 'F') and (objectproperty([object_id], 'CnstIsDisabled') = 1 or objectproperty([object_id], 'CnstIsNotTrusted') = 1)Devre dışı bırakılmış ve güvenilmeyen kısıtlamaları bulmak gibi bir sorgu çalıştırıyorum . Yukarıdaki gibi uygun alter tablosu ifadelerini yayınladıktan sonra, bu kısıtlamalar sorgudan kaybolur, böylece çalıştığını görebilirim.
Christian Hayter

2
@HenrikStaunPoulsen, bunun nedeni not_for_replication bayrağının 1'e ayarlanmasıdır. Bu, kısıtlamayı güvenilir değil yapar. SELECT name, create_date, modify_date, is_disabled, is_not_for_replication, is_not_trusted FROM sys.foreign_keys WHERE is_not_trusted = 1 Bu durumda kısıtlamayı bırakıp yeniden oluşturmanız gerekir. Bunu, gist.github.com/smoothdeveloper/ea48e43aead426248c0f'yi başarmak için kullanıyorum . Silme ve güncelleme sırasında bu komut dosyasında belirtilmediğini ve bunu dikkate almanız gerektiğini unutmayın.
kuklei

8

İşte bir VERİTABANINDA güvenilmeyen KISITLAMALARI tanımlamamıza ve düzeltmemize yardımcı olmak için yazdığım bazı kodlar. Her sorunu düzeltmek için kod üretir.

    ;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;
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.