Patrzę na przykładową bazę danych AdventureWorks dla SQL Server 2008 i widzę w ich skryptach do tworzenia, że zwykle używają następujących elementów:
ALTER TABLE [Production].[ProductCostHistory] WITH CHECK ADD
CONSTRAINT [FK_ProductCostHistory_Product_ProductID] FOREIGN KEY([ProductID])
REFERENCES [Production].[Product] ([ProductID])
GO
a zaraz po nim:
ALTER TABLE [Production].[ProductCostHistory] CHECK CONSTRAINT
[FK_ProductCostHistory_Product_ProductID]
GO
Widzę to dla kluczy obcych (tak jak tutaj), unikalnych ograniczeń i regularnych CHECK
ograniczeń; DEFAULT
ograniczenia używają zwykłego formatu, który jest mi bardziej znany, na przykład:
ALTER TABLE [Production].[ProductCostHistory] ADD CONSTRAINT
[DF_ProductCostHistory_ModifiedDate] DEFAULT (getdate()) FOR [ModifiedDate]
GO
Jaka jest różnica, jeśli w ogóle, między robieniem tego w pierwszy sposób a drugim?
źródło
ALTER TABLE foo NOCHECK CONSTRAINT fk_b
a następnie włączysz jeALTER TABLE foo CHECK CONSTRAINT fk_b
ponownie, nie weryfikuje ograniczenia.ALTER TABLE foo WITH CHECK CHECK CONSTRAINT fk_b
jest konieczne do zweryfikowania danych.Aby pokazać, jak to działa,
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;
źródło
DROP TABLE T2; DROP TABLE T1;
W nawiązaniu do powyższych doskonałych komentarzy na temat zaufanych ograniczeń:
select * from sys.foreign_keys where is_not_trusted = 1 ; select * from sys.check_constraints where is_not_trusted = 1 ;
Niezaufane ograniczenie, jak sugeruje jego nazwa, nie może być w tej chwili ufane, że dokładnie reprezentuje stan danych w tabeli. Można jednak, ale można ufać, że sprawdzi dane dodane i zmodyfikowane w przyszłości.
Ponadto niezaufane ograniczenia są ignorowane przez optymalizator zapytań.
Kod włączający ograniczenia sprawdzające i ograniczenia klucza obcego jest dość zły, z trzema znaczeniami słowa „sprawdź”.
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".
źródło
WITH NOCHECK
jest również używany, gdy w tabeli istnieją dane, które nie są zgodne z ograniczeniem zdefiniowanym i nie chcesz, aby było ono sprzeczne z nowym ograniczeniem, które implementujesz ...źródło
WITH CHECK
jest rzeczywiście zachowaniem domyślnym, jednak dobrym zwyczajem jest uwzględnienie go w kodowaniu.Alternatywne zachowanie jest oczywiście do zastosowania
WITH NOCHECK
, więc dobrze jest wyraźnie określić swoje zamiary. Jest to często używane, gdy grasz z / modyfikując / przełączając partycje wbudowane.źródło
Klucz obcy i ograniczenia sprawdzające mają koncepcję bycia zaufanym lub niezaufanym, a także włączania i wyłączania. Zobacz stronę MSDN, aby
ALTER TABLE
uzyskać szczegółowe informacje.WITH CHECK
jest wartością domyślną do dodawania nowego klucza obcego i ograniczeń sprawdzania,WITH NOCHECK
jest wartością domyślną do ponownego włączania wyłączonego klucza obcego i ograniczeń sprawdzania. Ważne jest, aby zdawać sobie sprawę z różnicy.Mimo to wszelkie pozornie zbędne instrukcje generowane przez narzędzia są po prostu dostępne dla bezpieczeństwa i / lub łatwości kodowania. Nie martw się o nich.
źródło
WITH CHECK CHECK CONSTRAINT
że zaufasz im.select * from sys.objects where [type] in ('C', 'F') and (objectproperty([object_id], 'CnstIsDisabled') = 1 or objectproperty([object_id], 'CnstIsNotTrusted') = 1)
znalezienie wyłączonych i niezaufanych ograniczeń. Po wydaniu odpowiednich instrukcji alter table, jak powyżej, ograniczenia te znikają z zapytania, więc widzę, jak działa.Oto kod, który napisałem, aby pomóc nam zidentyfikować i skorygować niezaufane OGRANICZENIA w BAZIE DANYCH. Generuje kod, aby naprawić każdy problem.
;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;
źródło