2009-02-09 19 views
106

Tôi nhìn vào cơ sở dữ liệu mẫu AdventureWorks cho SQL Server 2008, và tôi thấy trong kịch bản sáng tạo của họ rằng họ có xu hướng sử dụng như sau:WITH CHECK ADD CONSTRAINT tiếp theo ràng buộc kiểm tra vs Thanh CONSTRAINT

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

ngay sau đó:

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

tôi thấy điều này cho các phím nước ngoài (như ở đây), ràng buộc duy nhất và thường xuyên CHECK hạn chế; DEFAULT hạn chế sử dụng định dạng thường xuyên Tôi quen thuộc hơn với ví dụ như:

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

sự khác biệt là gì, nếu có, giữa làm nó theo cách đầu tiên so với thứ hai?

Trả lời

72

Cú pháp đầu tiên là thừa - WITH CHECK là mặc định cho các ràng buộc mới và ràng buộc được bật theo mặc định.

Cú pháp này được tạo bởi phòng quản lý SQL khi tạo tập lệnh sql - tôi giả định đó là một số dư thừa bổ sung, có thể đảm bảo ràng buộc được bật ngay cả khi hành vi ràng buộc mặc định cho bảng được thay đổi.

+9

Không giống như KIỂM TRA thực sự là mặc định, nó chỉ là mặc định cho dữ liệu mới. Từ http://msdn.microsoft.com/en-us/library/ms190273.aspx: "Nếu không được chỉ định, WITH CHECK được giả định cho các ràng buộc mới, và WITH NOCHECK được giả định cho các ràng buộc được kích hoạt lại." –

+3

@ZainRizvi: không phải dữ liệu mới, những hạn chế mới. Nếu bạn vô hiệu hóa một ràng buộc với 'ALTER TABLE foo NOCHECK CONSTRAINT fk_b' và sau đó kích hoạt lại nó với' ALTER TABLE foo CHECK CONSTRAINT fk_b', nó không kiểm tra ràng buộc. 'ALTER TABLE foo VỚI KIỂM TRA KIỂM TRA KIỂM TRA fk_b' là cần thiết để xác minh dữ liệu. – jmoreno

+1

Ban đầu tôi không đọc rõ điều này. Dòng thứ hai (dư thừa), là hàm để bật ràng buộc. Vì ràng buộc được bật theo mặc định, dòng thứ hai là thừa. – blindguy

12

WITH CHECK thực sự là hành vi mặc định, tuy nhiên, thực tiễn tốt là bao gồm trong mã của bạn.

Hành vi thay thế là tất nhiên để sử dụng WITH NOCHECK, vì vậy bạn nên xác định rõ ràng ý định của mình. Điều này thường được sử dụng khi bạn đang chơi với/sửa đổi/chuyển đổi phân vùng nội tuyến.

9

Ràng buộc khóa và kiểm tra ngoại khóa có khái niệm là đáng tin cậy hoặc không đáng tin cậy, cũng như được bật và tắt. Xem trang MSDN cho ALTER TABLE để biết chi tiết đầy đủ.

WITH CHECK là mặc định để thêm khóa ngoại và kiểm tra ràng buộc mới, WITH NOCHECK là mặc định cho bật lại khóa và khóa kiểm soát bị khóa. Điều quan trọng là phải nhận thức được sự khác biệt.

Có nói rằng, bất kỳ câu lệnh rõ ràng dự phòng nào được tạo ra bởi các tiện ích chỉ đơn giản là có an toàn và/hoặc dễ dàng mã hóa. Đừng lo lắng về chúng.

+0

Có phải liên kết này bạn đang đề cập đến: https://msdn.microsoft.com/en-us/library/ms190273.aspx? Điều này có nghĩa là chúng ta phải làm một bảng ALTER TABLE với KIỂM TRA KIỂM TRA KIỂM TRA TẤT CẢ thay vì làm điều đó cho mỗi ràng buộc? –

+0

@HenrikStaunPoulsen: Vâng, đó là liên kết. Không có gì ngăn cản bạn cho phép mỗi ràng buộc riêng lẻ, nhưng bạn phải nói 'VỚI KIỂM TRA KIỂM TRA KIỂM TRA' để có được chúng đáng tin cậy. –

+0

Tôi đã thử điều đó; Tôi chạy "ALTER TABLE [dfm]. [TRATransformError] VỚI CHECK KIỂM TRA KIỂM TRA [FK_TRATransformError_ETLEvent]". Nhưng FK vẫn có Is_Not_Trusted = 1. Sau đó, tôi bỏ FK, và tái tạo nó với "WITH CHECK CHECK", và bây giờ tôi có Is_Not_Trusted = 0. Cuối cùng. Bạn có biết tại sao? Xin lưu ý rằng tôi luôn có is_not_for_replication = 0 –

13

WITH NOCHECK cũng được sử dụng khi có dữ liệu hiện có trong bảng không tuân theo ràng buộc như được xác định và bạn không muốn nó chạy afoul ràng buộc mới mà bạn đang triển khai ...

37

để chứng minh cách này 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; 
+3

Dọn dẹp - 'DROP TABLE T2; DROP TABLE T1; ' – Graeme

+4

Tôi đã thêm mã làm sạch từ nhận xét của bạn vào câu trả lời thực tế của bạn để hỗ trợ các bản sao và tạp chí qua đêm ở ngoài đó. – mwolfe02

+14

"Bản sao-và-đêm" có vẻ hơi tiêu cực. Tôi sẽ xem xét bản thân mình một trong những người sử dụng ngăn xếp (cho từ ngữ tích cực hơn ...) "những người tìm thấy những loại ví dụ chi tiết cực kỳ có giá trị". – GaTechThomas

19

tiếp tục cho ý kiến ​​tuyệt vời ở trên về những hạn chế đáng tin cậy:

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

Một hạn chế không tin cậy , giống như tên gọi của nó, không thể tin cậy để thể hiện chính xác trạng thái của dữ liệu trong bảng ngay bây giờ. Nó có thể, tuy nhiên, nhưng có thể được tin cậy để kiểm tra dữ liệu được thêm vào và sửa đổi trong tương lai.

Ngoài ra, các ràng buộc không tin cậy được bỏ qua bởi trình tối ưu hóa truy vấn.

Mã để bật ràng buộc kiểm tra và ràng buộc khóa ngoài là khá xấu, với ba nghĩa của từ "kiểm tra".

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

Dưới đây là một số mã tôi đã viết để giúp chúng tôi xác định và sửa chữa CONSTRAINT không đáng tin cậy trong DATABASE. Nó tạo mã để khắc phục từng vấn đề.

;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; 
Các vấn đề liên quan