CON CONTROLLA AGGIUNGI VINCITORE seguito da CONTROLLA VINCERE vs AGGIUNGI VINCITORE


133

Sto esaminando il database di esempio AdventureWorks per SQL Server 2008 e vedo nei loro script di creazione che tendono a utilizzare quanto segue:

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

seguito immediatamente da:

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

Vedo questo per chiavi esterne (come qui), vincoli univoci e CHECKvincoli regolari ; DEFAULTi vincoli utilizzano il formato normale con cui ho più familiarità come:

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

Qual è la differenza, se esiste, tra farlo nel primo modo rispetto al secondo?

Risposte:


94

La prima sintassi è ridondante: WITH CHECK è predefinito per i nuovi vincoli e anche il vincolo è attivato per impostazione predefinita.

Questa sintassi viene generata dallo studio di gestione SQL durante la generazione di script sql: suppongo sia una sorta di ridondanza aggiuntiva, probabilmente per garantire che il vincolo sia abilitato anche se viene modificato il comportamento del vincolo predefinito per una tabella.


12
Non sembra che WITH CHECK sia in realtà il valore predefinito, è solo il valore predefinito per i nuovi dati. Da msdn.microsoft.com/en-us/library/ms190273.aspx : "Se non specificato, WITH CHECK viene assunto per i nuovi vincoli e WITH NOCHECK viene assunto per i vincoli riattivati."
Zain Rizvi,

8
@ZainRizvi: non nuovi dati, nuovi vincoli. Se si disabilita un vincolo con ALTER TABLE foo NOCHECK CONSTRAINT fk_be quindi si riattiva con ALTER TABLE foo CHECK CONSTRAINT fk_besso non si verifica il vincolo. ALTER TABLE foo WITH CHECK CHECK CONSTRAINT fk_bè necessario per far verificare i dati.
jmoreno,

2
Non era chiaro per me leggere questo inizialmente. La seconda riga (ridondante) è la funzione per attivare il vincolo. Poiché il vincolo è attivo per impostazione predefinita, la seconda riga è ridondante.
cieco

47

Per dimostrare come funziona--

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
Pulisci ... DROP TABLE T2; DROP TABLE T1;
Graeme,

8
Ho aggiunto il codice di pulizia dal tuo commento alla tua risposta effettiva per assistere i copia-e-pasters fly-by-night là fuori.
mwolfe02,

18
"Fly-by-night copy-and-pasters" sembra un po 'negativo. Mi considererei uno di quegli utenti dello stack (per una formulazione più positiva ...) "che trovano estremamente utili questi tipi di esempi dettagliati".
GaTechThomas

2
Derogatorio o no, "volare di notte" sembra che mi descriva perfettamente.
sanepete,

21

A seguito di questi eccellenti commenti sui vincoli di fiducia:

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

Un vincolo non attendibile, come suggerisce il nome, non può essere considerato attendibile per rappresentare con precisione lo stato dei dati nella tabella in questo momento. Tuttavia, può essere considerato attendibile per controllare i dati aggiunti e modificati in futuro.

Inoltre, l'ottimizzatore delle query non tiene conto dei vincoli non attendibili.

Il codice per abilitare i vincoli di controllo e i vincoli di chiave esterna è piuttosto male, con tre significati della parola "controllo".

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 viene utilizzato anche quando in una tabella sono presenti dati che non sono conformi al vincolo definito e non si desidera che si verifichino i nuovi vincoli che si stanno implementando ...


13

WITH CHECK è in effetti il ​​comportamento predefinito, tuttavia è buona norma includere nella codifica.

Il comportamento alternativo è ovviamente da usare WITH NOCHECK, quindi è bene definire esplicitamente le tue intenzioni. Questo è spesso usato quando stai giocando / modificando / cambiando le partizioni in linea.


9

I vincoli di controllo e chiave esterna hanno il concetto di essere attendibili o non attendibili, nonché di essere abilitati e disabilitati. Vedere la pagina MSDN ALTER TABLEper i dettagli completi.

WITH CHECK è l'impostazione predefinita per l'aggiunta di una nuova chiave esterna e per controllare i vincoli, WITH NOCHECK è l'impostazione predefinita per la riattivazione della chiave esterna disabilitata e per i vincoli di verifica. È importante essere consapevoli della differenza.

Detto questo, tutte le dichiarazioni apparentemente ridondanti generate dalle utility sono semplicemente lì per sicurezza e / o facilità di codifica. Non preoccuparti per loro.


È questo link a cui ti riferisci: msdn.microsoft.com/en-us/library/ms190273.aspx ? Questo significa che dobbiamo fare una tabella ALTER TABLE CON CHECK CHECK VINCERE TUTTO invece di farlo per ogni vincolo?
Henrik Staun Poulsen,

@HenrikStaunPoulsen: Sì, questo è il link. Non c'è nulla che ti fermi abilitando ogni vincolo individualmente, ma devi dire WITH CHECK CHECK CONSTRAINTper farli fidare.
Christian Hayter,

Ci ho provato; Ho eseguito "ALTER TABLE [dfm]. [TRATransformError] CON CHECK CHECK CONSTRAINT [FK_TRATransformError_ETLEvent]". Ma l'FK ha ancora Is_Not_Trusted = 1. Quindi ho lasciato cadere l'FK e l'ho ricreato con "WITH CHECK CHECK", e ora ho Is_Not_Trusted = 0. Alla fine. Sai perché? Si prega di notare che ho sempre avuto is_not_for_replication = 0
Henrik Staun Poulsen

@HenrikStaunPoulsen: Non lo so, ha sempre funzionato bene per me. Eseguo una query come select * from sys.objects where [type] in ('C', 'F') and (objectproperty([object_id], 'CnstIsDisabled') = 1 or objectproperty([object_id], 'CnstIsNotTrusted') = 1)per trovare vincoli disabilitati e non attendibili. Dopo aver emesso le istruzioni appropriate della tabella alter come sopra, tali vincoli scompaiono dalla query, quindi posso vederlo funzionare.
Christian Hayter,

2
@HenrikStaunPoulsen, è perché il flag not_for_replication è impostato su 1. Questo rende il vincolo come non attendibile. SELEZIONA nome, create_date, edit_date, is_disabled, is_not_for_replication, is_not_trusted FROM sys.foreign_keys WHERE is_not_trusted = 1 In tal caso è necessario eliminare e ricreare il vincolo. Lo uso per realizzare che gist.github.com/smoothdeveloper/ea48e43aead426248c0f Tieni presente che in questo script non sono specificati sull'eliminazione e sull'aggiornamento e devi tenerne conto.
Kuklei,

8

Ecco del codice che ho scritto per aiutarci a identificare e correggere VINCOLI non attendibili in un DATABASE. Genera il codice per risolvere ogni problema.

    ;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;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.