2009-02-09 14 views
106

estoy mirando a la base de datos de ejemplo AdventureWorks de SQL Server 2008, y veo en sus scripts de creación que tienden a utilizar lo siguiente:con la restricción de verificación Agregar seguido de restricción CHECK CONSTRAINT vs ADD

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

seguido inmediatamente por:

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

veo esto para las claves externas (como en este caso), y restricciones únicas limitaciones CHECK regulares; DEFAULT limitaciones utilizan el formato normal estoy más familiarizado con tales como:

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

¿Cuál es la diferencia, si la hay, entre hacerlo de la primera forma con respecto al segundo?

Respuesta

72

La primera sintaxis es redundante: el WITH CHECK es el predeterminado para nuevas restricciones y la restricción también está activada por defecto.

Esta sintaxis es generada por el SQL Management Studio al generar scripts sql - Supongo que es una especie de redundancia adicional, posiblemente para asegurar que la restricción esté habilitada incluso si se modifica el comportamiento de restricción predeterminado para una tabla.

+9

Parece que WITH CHECK no es el valor predeterminado, solo es el valor predeterminado para los datos nuevos. Desde http://msdn.microsoft.com/en-us/library/ms190273.aspx: "Si no se especifica, se supone que WITH CHECK para las nuevas restricciones, y WITH NOCHECK se supone para las restricciones habilitadas nuevamente". –

+3

@ZainRizvi: no hay datos nuevos, nuevas restricciones. Si desactiva una restricción con 'ALTER TABLE foo NOCHECK CONSTRAINT fk_b' y luego la vuelve a habilitar con' ALTER TABLE foo CHECK CONSTRAINT fk_b', no verifica la restricción. 'ALTER TABLE foo WITH CHECK CHECK CONSTRAINT fk_b' es necesario para que se verifiquen los datos. – jmoreno

+1

No estaba claro para mí leer esto inicialmente. La segunda línea (redundante) es la función para activar la restricción. Como la restricción está activada por defecto, la segunda línea es redundante. – blindguy

12

WITH CHECK es de hecho el comportamiento predeterminado, sin embargo, es una buena práctica incluirlo en su código.

El comportamiento alternativo es, por supuesto, utilizar WITH NOCHECK, por lo que es bueno definir explícitamente sus intenciones. Esto se usa a menudo cuando está jugando con/modificando/cambiando particiones en línea.

9

Las restricciones foráneas de clave y verificación tienen el concepto de ser de confianza o no de confianza, así como de habilitarse y deshabilitarse. Consulte la página MSDN para ALTER TABLE para obtener más información.

WITH CHECK es el valor predeterminado para agregar nuevas claves foráneas y restricciones de verificación, WITH NOCHECK es el valor predeterminado para volver a habilitar la clave externa deshabilitada y comprobar las restricciones. Es importante ser consciente de la diferencia.

Habiendo dicho eso, cualquier declaración aparentemente redundante generada por las utilidades está ahí simplemente por seguridad y/o facilidad de codificación. No te preocupes por ellos

+0

¿Este es el enlace al que se refiere: https://msdn.microsoft.com/en-us/library/ms190273.aspx? ¿Esto significa que tenemos que hacer una tabla ALTER TABLE CON CHECK CHECK CONSTRAINT ALL en lugar de hacerlo para cada restricción? –

+0

@HenrikStaunPoulsen: Sí, ese es el enlace. No hay nada que te impida habilitar cada restricción individualmente, pero tienes que decir 'CON CHECK CHECK CONSTRAINT 'para que sean confiables. –

+0

Lo intenté; Ejecuté "ALTER TABLE [dfm]. [TRATransformError] CON CHECK CHECK CONSTRAINT [FK_TRATransformError_ETLEvent]". Pero el FK todavía tiene Is_Not_Trusted = 1. Luego dejé caer el FK y lo recreé con "WITH CHECK CHECK", y ahora tengo Is_Not_Trusted = 0. Al final. ¿Sabes por qué? Tenga en cuenta que siempre he tenido is_not_for_replication = 0 –

13

WITH NOCHECK se utiliza también cuando uno ha de datos existentes en una tabla que no se ajusta a la restricción como se define y que no quieren que se ejecute en conflicto con la nueva restricción de que va a implementar ...

37

para demostrar cómo esta works--

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; 
+3

Limpieza: 'DROP TABLE T2; DROP TABLE T1; ' – Graeme

+4

Agregué el código de limpieza de su comentario a su respuesta real para ayudar a copiar y pegar todo por la noche. – mwolfe02

+14

"Copiar y pegar poco a poco" parece un poco negativo. Me consideraría uno de esos usuarios de la pila (para una redacción más positiva ...) "que encuentran este tipo de ejemplos detallados extremadamente valiosos". – GaTechThomas

19

Además de los excelentes comentarios anteriores sobre las limitaciones de confianza:

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

una restricción que no se confía , como sugiere su nombre, no se puede confiar en que represente con precisión el estado de los datos en la tabla en este momento. Sin embargo, se puede confiar en que se verifiquen los datos agregados y modificados en el futuro.

Además, el optimizador de consultas no tiene en cuenta las restricciones que no son de confianza.

El código para habilitar restricciones de verificación y restricciones de clave externa es bastante malo, con tres significados de la palabra "verificar".

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". 
5

Aquí hay un código que escribí para ayudar a identificar y corregir las limitaciones que no se confía en una base. Genera el código para arreglar cada 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; 
Cuestiones relacionadas