AVEC CHECK ADD CONSTRAINT suivi de CHECK CONSTRAINT vs ADD CONSTRAINT

133

Je regarde l'exemple de base de données AdventureWorks pour SQL Server 2008 et je vois dans leurs scripts de création qu'ils ont tendance à utiliser les éléments suivants:

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

suivi immédiatement par:

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

Je vois cela pour les clés étrangères (comme ici), les contraintes uniques et les CHECKcontraintes régulières ; DEFAULTles contraintes utilisent le format régulier que je connais mieux, tel que:

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

Quelle est la différence, le cas échéant, entre le faire de la première manière et de la seconde?

Wayne Molina
la source

Réponses:

94

La première syntaxe est redondante - WITH CHECK est la valeur par défaut pour les nouvelles contraintes, et la contrainte est également activée par défaut.

Cette syntaxe est générée par le studio de gestion SQL lors de la génération de scripts SQL - je suppose qu'il s'agit d'une sorte de redondance supplémentaire, éventuellement pour garantir que la contrainte est activée même si le comportement de contrainte par défaut pour une table est modifié.

Chris Hynes
la source
12
Il ne semble pas que WITH CHECK soit en fait la valeur par défaut, ce n'est que la valeur par défaut pour les nouvelles données. De msdn.microsoft.com/en-us/library/ms190273.aspx : «Si non spécifié, WITH CHECK est supposé pour les nouvelles contraintes et WITH NOCHECK est supposé pour les contraintes réactivées.
Zain Rizvi
8
@ZainRizvi: pas de nouvelles données, de nouvelles contraintes. Si vous désactivez une contrainte avec ALTER TABLE foo NOCHECK CONSTRAINT fk_bpuis la réactivez avec ALTER TABLE foo CHECK CONSTRAINT fk_belle ne vérifie pas la contrainte. ALTER TABLE foo WITH CHECK CHECK CONSTRAINT fk_best nécessaire pour faire vérifier les données.
jmoreno
2
Ce n'était pas clair pour moi de lire ceci au départ. La deuxième ligne (redondante), est la fonction pour activer la contrainte. La contrainte étant activée par défaut, la deuxième ligne est redondante.
blindguy
47

Pour démontrer comment cela fonctionne -

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;
Graeme
la source
7
Clean up-- DROP TABLE T2; DROP TABLE T1;
Graeme
8
J'ai ajouté le code de nettoyage de votre commentaire à votre réponse réelle pour aider les copieurs et copieurs de nuit là-bas.
mwolfe02
18
"Fly-by-night copy-and-pasters" semble un peu négatif. Je me considérerais comme l'un de ces utilisateurs de pile (pour une formulation plus positive ...) "qui trouvent ces types d'exemples détaillés extrêmement précieux".
GaTechThomas
2
Dérogatoire ou pas, «voler de nuit» me donne l'impression de me décrire parfaitement.
sanepete
21

Suite aux excellents commentaires ci-dessus sur les contraintes de confiance:

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

Une contrainte non approuvée, comme son nom l'indique, ne peut pas être fiable pour représenter avec précision l'état des données dans la table en ce moment. Il peut cependant, mais peut être fiable, vérifier les données ajoutées et modifiées à l'avenir.

De plus, l'optimiseur de requêtes ne tient pas compte des contraintes non fiables.

Le code pour activer les contraintes de vérification et les contraintes de clé étrangère est assez mauvais, avec trois significations du mot «vérifier».

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".
Marcheur de Greenstone
la source
15

WITH NOCHECK est également utilisé lorsque l'on a des données existantes dans une table qui ne sont pas conformes à la contrainte telle que définie et que vous ne voulez pas qu'elle soit contraire à la nouvelle contrainte que vous implémentez ...

midi et
la source
13

WITH CHECK est en effet le comportement par défaut, mais il est recommandé de l'inclure dans votre codage.

Le comportement alternatif est bien sûr à utiliser WITH NOCHECK, il est donc bon de définir explicitement vos intentions. Ceci est souvent utilisé lorsque vous jouez avec / modifiez / changez de partitions en ligne.

John Sansom
la source
9

Les contraintes de clé étrangère et de vérification ont le concept d'être approuvées ou non approuvées, ainsi que d'être activées et désactivées. Consultez la page MSDN pour plus ALTER TABLEde détails.

WITH CHECKest la valeur par défaut pour ajouter une nouvelle clé étrangère et vérifier les contraintes, WITH NOCHECKest la valeur par défaut pour réactiver la clé étrangère désactivée et vérifier les contraintes. Il est important d'être conscient de la différence.

Cela dit, toutes les déclarations apparemment redondantes générées par les utilitaires sont simplement là pour la sécurité et / ou la facilité de codage. Ne t'inquiète pas pour eux.

Christian Hayter
la source
Est-ce ce lien auquel vous faites référence: msdn.microsoft.com/en-us/library/ms190273.aspx ? Cela signifie-t-il que nous devons faire une table ALTER TABLE WITH CHECK CHECK CONSTRAINT ALL au lieu de le faire pour chaque contrainte?
Henrik Staun Poulsen
@HenrikStaunPoulsen: Oui, c'est le lien. Rien ne vous empêche d'activer chaque contrainte individuellement, mais vous devez dire WITH CHECK CHECK CONSTRAINTpour leur faire confiance.
Christian Hayter
J'ai essayé ça; J'ai lancé "ALTER TABLE [dfm]. [TRATransformError] WITH CHECK CHECK CONSTRAINT [FK_TRATransformError_ETLEvent]". Mais le FK a toujours Is_Not_Trusted = 1. Ensuite, j'ai laissé tomber le FK et je l'ai recréé avec "WITH CHECK CHECK", et maintenant j'ai Is_Not_Trusted = 0. Enfin. Est-ce que tu sais pourquoi? Veuillez noter que j'ai toujours eu is_not_for_replication = 0
Henrik Staun Poulsen
@HenrikStaunPoulsen: Je ne sais pas, ça a toujours bien fonctionné pour moi. J'exécute une requête comme select * from sys.objects where [type] in ('C', 'F') and (objectproperty([object_id], 'CnstIsDisabled') = 1 or objectproperty([object_id], 'CnstIsNotTrusted') = 1)pour trouver des contraintes désactivées et non fiables. Après avoir émis les instructions alter table appropriées comme ci-dessus, ces contraintes disparaissent de la requête, donc je peux le voir fonctionner.
Christian Hayter
2
@HenrikStaunPoulsen, c'est parce que l'indicateur not_for_replication est défini sur 1. Cela rend la contrainte non approuvée. SELECT nom, create_date, modify_date, is_disabled, is_not_for_replication, is_not_trusted FROM sys.foreign_keys WHERE is_not_trusted = 1 Dans ce cas, vous devez supprimer et recréer la contrainte. J'utilise ceci pour accomplir cela gist.github.com/smoothdeveloper/ea48e43aead426248c0f Gardez à l'esprit que la suppression et la mise à jour ne sont pas spécifiées dans ce script et que vous devez en tenir compte.
kuklei le
8

Voici un code que j'ai écrit pour nous aider à identifier et à corriger les CONTRAINTES non fiables dans une BASE DE DONNÉES. Il génère le code pour résoudre chaque problème.

    ;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;
Graeme
la source