VỚI KIỂM TRA THÊM CONSTRAINT theo sau là KIỂM TRA CONSTRAINT so với ADD CONSTRAINT


133

Tôi đang xem cơ sở dữ liệu mẫu AdventureWorks cho SQL Server 2008 và tôi thấy trong các tập lệnh tạo của họ có xu hướng sử dụng như sau:

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

theo ngay lập tức bởi:

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

Tôi thấy điều này đối với các khóa ngoại (như ở đây), các ràng buộc duy nhất và các CHECKràng buộc thông thường ; DEFAULTcác ràng buộc sử dụng định dạng thông thường mà tôi quen thuộc hơn như:

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

Sự khác biệt, nếu có, giữa việc thực hiện theo cách thứ nhất so với cách thứ hai là gì?

Câu trả lời:


94

Cú pháp đầu tiên là không cần thiết - CHECK mặc định cho các ràng buộc mới và ràng buộc cũng được bật theo mặc định.

Cú pháp này được tạo bởi studio quản lý SQL khi tạo tập lệnh sql - Tôi giả sử đó là một loại dư thừa, 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.


12
Có vẻ như KHÔNG KIỂM TRA thực sự là mặc định, nó chỉ là mặc định cho dữ liệu mới. Từ msdn.microsoft.com/en-us/l Library / ms190273.aspx : "Nếu không được chỉ định, VỚI CHECK được giả sử cho các ràng buộc mới và VỚI NOCHECK được giả sử cho các ràng buộc được kích hoạt lại."
Zain Rizvi

8
@ZainRizvi: không phải dữ liệu mới, ràng buộc 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_bvà sau đó kích hoạt lại nó với ALTER TABLE foo CHECK CONSTRAINT fk_bnó thì không xác minh ràng buộc đó. ALTER TABLE foo WITH CHECK CHECK CONSTRAINT fk_blà cần thiết để có dữ liệu được xác minh.
jmoreno 14/03/2015

2
Nó không rõ ràng cho tôi đọc điều này ban đầu. Dòng thứ hai (dự phòng), là chức năng bật ràng buộc. Vì ràng buộc được bật theo mặc định, dòng thứ hai là dự phòng.
blindguy

47

Để chứng minh làm thế nào điều này hoạt động--

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
Dọn dẹp-- DROP TABLE T2; DROP TABLE T1;
Graeme

8
Tôi đã thêm mã dọn dẹp 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à bản sao bay qua đêm.
mwolfe02

18
"Sao chép và bay qua đêm" có vẻ hơi tiêu cực. Tôi sẽ coi mình là một trong những người dùng ngăn xếp đó (đối với 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 này cực kỳ có giá trị".
GaTechThomas

2
Xúc phạm hay không, 'bay qua đêm' cảm giác như nó mô tả tôi một cách hoàn hảo.
sanepete

21

Hơn nữa với các ý kiến ​​tuyệt vời ở trên về các ràng buộc đá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 ràng buộc không đá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ờ. Tuy nhiên, nó có thể được tin cậy để kiểm tra dữ liệu được thêm và sửa đổi trong tương lai.

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

Mã để kích hoạt các ràng buộc kiểm tra và các ràng buộc khóa ngoại là khá tệ, 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".

15

WITH NOCHECK cũng được sử dụng khi một người có dữ liệu hiện có trong một 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 theo giới hạn mới mà bạn đang thực hiện ...


13

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

Các hành vi thay thế là tất nhiên để sử dụng WITH NOCHECK, vì vậy nó là tốt để xác định rõ ràng ý định của bạn. Đ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

Các ràng buộc khóa và kiểm tra 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 ALTER TABLEđể biết chi tiết đầy đủ.

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

Phải nói rằng, bất kỳ tuyên bố dư thừa rõ ràng nào được tạo ra bởi các tiện ích chỉ đơn giản là ở đó để đảm bảo an toàn và / hoặc dễ dàng mã hóa. Đừng lo lắng về họ.


Đây có phải là liên kết mà bạn đang tham khảo: msdn.microsoft.com/en-us/l Library / ms190273.aspx ? Điều này có nghĩa là chúng ta phải thực hiện một bảng ALTER TABLE VỚI KIỂM TRA KIỂM TRA CONSTRAINT TẤT CẢ thay vì làm điều đó cho mỗi ràng buộc?
Henrik Staun Poulsen

@HenrikStaunPoulsen: Vâng, đó là liên kết. Không có gì ngăn cản bạn kích hoạt từng ràng buộc riêng lẻ, nhưng bạn phải nói WITH CHECK CHECK CONSTRAINTđể khiến chúng tin cậy.
Christian Hayter

Tôi đã thử điều đó; Tôi đã chạy "ALTER TABLE [dfm]. [TRATransformError] VỚI KIỂM TRA KIỂM TRA CONSTRAINT [FK_TRATransformError_ETLEvent]". Nhưng FK vẫn có Is_Not_Trusty = 1. Sau đó, tôi đã bỏ FK và tạo lại nó với "VỚI KIỂM TRA KIỂM TRA", và bây giờ tôi có Is_Not_Trusty = 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_Vplication = 0
Henrik Staun Poulsen

@HenrikStaunPoulsen: Tôi không biết, nó luôn hoạt động tốt với tôi. Tôi chạy một truy vấn như select * from sys.objects where [type] in ('C', 'F') and (objectproperty([object_id], 'CnstIsDisabled') = 1 or objectproperty([object_id], 'CnstIsNotTrusted') = 1)để tìm các ràng buộc bị vô hiệu hóa và không đáng tin cậy. Sau khi đưa ra các câu lệnh thay đổi bảng thích hợp như trên, các ràng buộc đó sẽ biến mất khỏi truy vấn, vì vậy tôi có thể thấy nó hoạt động.
Christian Hayter

2
@HenrikStaunPoulsen, đó là do cờ not_for_Vplication được đặt thành 1. Điều này làm cho các ràng buộc là không đáng tin cậy. Tên CHỌN, created_date, redirect_date, is_disables, is_not_for_Vplication, is_not_trusty TỪ sys.forign_keys WHERE is_not_trusty = 1 Trong trường hợp đó, bạn cần bỏ và tạo lại ràng buộc. Tôi sử dụng điều này để thực hiện gist.github.com/smoothdeveloper/ea48e43aead426248c0f Hãy nhớ rằng khi xóa và cập nhật không được chỉ định trong tập lệnh này và bạn cần tính đến điều đó.
kuklei

8

Dưới đây là một số mã tôi đã viết để giúp chúng tôi xác định và sửa các CONSTRAINT không đáng tin cậy trong một cơ sở dữ liệu. Nó tạo mã để sửa 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;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.