ด้วย CHECK ADD CONSTRAINT ตามด้วย CHECK CONSTRAINT เทียบกับ ADD CONSTRAINT


135

ฉันกำลังดูฐานข้อมูลตัวอย่าง AdventureWorks สำหรับ SQL Server 2008 และฉันเห็นในสคริปต์การสร้างของพวกเขาว่าพวกเขามักจะใช้สิ่งต่อไปนี้:

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

อะไรคือความแตกต่างระหว่างการทำวิธีแรกกับวิธีที่สอง?

คำตอบ:


96

ไวยากรณ์แรกซ้ำซ้อน - WITH CHECK เป็นค่าเริ่มต้นสำหรับข้อ จำกัด ใหม่และข้อ จำกัด จะเปิดใช้งานตามค่าเริ่มต้นเช่นกัน

ไวยากรณ์นี้สร้างขึ้นโดยสตูดิโอการจัดการ SQL เมื่อสร้างสคริปต์ sql - ฉันคิดว่ามันเป็นความซ้ำซ้อนพิเศษบางประเภทอาจเพื่อให้แน่ใจว่ามีการเปิดใช้งานข้อ จำกัด แม้ว่าจะมีการเปลี่ยนแปลงพฤติกรรมข้อ จำกัด เริ่มต้นสำหรับตาราง


12
ดูเหมือนว่าจริงๆแล้วการตรวจสอบจะไม่เป็นค่าเริ่มต้น แต่เป็นเพียงค่าเริ่มต้นสำหรับข้อมูลใหม่ จากmsdn.microsoft.com/en-us/library/ms190273.aspx : "หากไม่ได้ระบุ WITH CHECK จะถือว่าเป็นข้อ จำกัด ใหม่และด้วย 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

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

8
ฉันได้เพิ่มรหัสการล้างข้อมูลจากความคิดเห็นของคุณไปยังคำตอบที่แท้จริงของคุณเพื่อช่วยเหลือผู้คัดลอกและวางเอกสารแบบบินต่อคืน
mwolfe02

18
"Fly-by-night copy-and-pasters" ดูเหมือนจะเป็นแง่ลบเล็กน้อย ฉันคิดว่าตัวเองเป็นหนึ่งในผู้ใช้สแต็กเหล่านั้น (สำหรับคำพูดเชิงบวกมากกว่า ... ) "ที่พบว่าตัวอย่างโดยละเอียดประเภทนี้มีค่าอย่างยิ่ง"
GaTechThomas

2
เสื่อมเสียหรือไม่ 'บินกลางคืน' รู้สึกว่ามันอธิบายฉันได้อย่างสมบูรณ์แบบ
sanepete

21

นอกเหนือจากความคิดเห็นที่ยอดเยี่ยมข้างต้นเกี่ยวกับข้อ จำกัด ที่เชื่อถือได้:

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

ไม่สามารถเชื่อถือข้อ จำกัด ที่ไม่น่าเชื่อถือเท่าที่ชื่อแนะนำเพื่อแสดงสถานะของข้อมูลในตารางได้อย่างถูกต้องในขณะนี้ อย่างไรก็ตามสามารถตรวจสอบข้อมูลที่เพิ่มและแก้ไขได้ในอนาคต

นอกจากนี้ข้อ จำกัด ที่ไม่น่าเชื่อถือจะถูกมองข้ามโดยเครื่องมือเพิ่มประสิทธิภาพการสืบค้น

รหัสเพื่อเปิดใช้ข้อ จำกัด การตรวจสอบและข้อ จำกัด ของคีย์ต่างประเทศนั้นค่อนข้างแย่โดยมีสามความหมายของคำว่า "check"

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 table กับ CHECK CHECK CONSTRAINT ALL แทนที่จะทำตามข้อ จำกัด แต่ละข้อใช่หรือไม่?
Henrik Staun Poulsen

@HenrikStaunPoulsen: ใช่นั่นคือลิงค์ ไม่มีอะไรหยุดคุณเปิดใช้งานข้อ จำกัด แต่ละข้อ แต่คุณต้องพูดWITH CHECK CHECK CONSTRAINTเพื่อให้พวกเขาเชื่อถือได้
Christian Hayter

ฉันลองแล้ว; ฉันเรียกใช้ "ALTER TABLE [dfm] [TRATransformError] ด้วยการตรวจสอบการตรวจสอบ CONSTRAINT [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)เพื่อค้นหาข้อ จำกัด ที่ปิดใช้งานและไม่น่าเชื่อถือ หลังจากออกคำสั่งตารางแก้ไขที่เหมาะสมตามข้างต้นข้อ จำกัด เหล่านั้นจะหายไปจากแบบสอบถามดังนั้นฉันจึงเห็นว่ามันใช้งานได้
Christian Hayter

2
@HenrikStaunPoulsen เนื่องจากแฟล็ก not_for_replication ถูกตั้งค่าเป็น 1 ทำให้ข้อ จำกัด ไม่น่าเชื่อถือ SELECT name, create_date, modified_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

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.