С CHECK ADD CONSTRAINT и CHECK CONSTRAINT против ADD CONSTRAINT - PullRequest
118 голосов
/ 10 февраля 2009

Я просматриваю пример базы данных 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

Какая разница, если таковая имеется, между первым и вторым?

Ответы [ 7 ]

86 голосов
/ 10 февраля 2009

Первый синтаксис избыточен - WITH CHECK является значением по умолчанию для новых ограничений, и ограничение также включено по умолчанию.

Этот синтаксис генерируется студией управления SQL при создании сценариев sql - я предполагаю, что это какая-то дополнительная избыточность, возможно, чтобы гарантировать, что ограничение включено, даже если поведение ограничения по умолчанию для таблицы изменено. *

39 голосов
/ 13 сентября 2012

Чтобы продемонстрировать, как это работает -

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;
20 голосов
/ 11 марта 2013

В дополнение к вышеупомянутым превосходным комментариям о доверенных ограничениях:

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

Ненадежному ограничению, как и следует из его названия, нельзя доверять для точного представления состояния данных в таблице прямо сейчас. Однако можно, но можно доверять проверять данные, добавленные и измененные в будущем.

Кроме того, оптимизатор запросов игнорирует ненадежные ограничения.

Код для включения проверочных ограничений и ограничений внешнего ключа довольно плохой, с тремя значениями слова «проверка».

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 голосов
/ 31 августа 2010

WITH NOCHECK также используется, когда в таблице есть данные, которые не соответствуют определенному ограничению, и вы не хотите, чтобы он противоречил вашему новому ограничению ...

13 голосов
/ 10 февраля 2009

WITH CHECK действительно является поведением по умолчанию, однако рекомендуется включить его в код.

Альтернативное поведение, конечно, использовать WITH NOCHECK, так что хорошо бы четко определить ваши намерения. Это часто используется, когда вы играете / модифицируете / переключаете встроенные разделы.

9 голосов
/ 14 октября 2009

Ограничения внешнего ключа и проверки имеют концепцию доверия или недоверия, а также включения и отключения. См. Страницу MSDN для ALTER TABLE для получения полной информации.

WITH CHECK является значением по умолчанию для добавления нового внешнего ключа и проверки ограничений, WITH NOCHECK является значением по умолчанию для повторного включения отключенного внешнего ключа и проверки ограничений. Важно осознавать разницу.

Сказав это, любые явно избыточные операторы, сгенерированные утилитами, просто существуют для безопасности и / или простоты кодирования. Не беспокойся о них.

7 голосов
/ 15 сентября 2015

Вот некоторый код, который я написал, чтобы помочь нам идентифицировать и исправить недоверенные ОГРАНИЧЕНИЯ в БАЗЕ ДАННЫХ. Он генерирует код для устранения каждой проблемы.

    ;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;
...