sql >> Base de Datos >  >> RDS >> Sqlserver

Lo que debe saber sobre WITH NOCHECK al habilitar una restricción CHECK en SQL Server

Si alguna vez te encuentras en la situación en la que necesitas volver a habilitar un CHECK restricción que se ha deshabilitado previamente, definitivamente debe asegurarse de saber lo que está haciendo.

En particular, debe comprender la diferencia entre WITH NOCHECK y WITH CHECK argumentos.

Estos argumentos se pueden utilizar en el momento de habilitar la restricción. Especifican si los datos existentes se validan o no contra su CHECK rehabilitado (o recién agregado) restricción. Básicamente, tiene la opción de verificar todos los datos existentes en busca de violaciones de la restricción. Si no especifica nada, los datos existentes no ser revisado Por eso es importante entender cómo funciona.

Por cierto, estos argumentos también se aplican a las restricciones de clave externa.

Como era de esperar, WITH CHECK especifica que los datos existentes se validan y WITH NOCHECK especifica que no lo es. El valor predeterminado es WITH NOCHECK .

Si usa WITH NOCHECK , la restricción se marcará como no confiable. En realidad, se marca como no confiable cuando deshabilita la restricción. Pero cuando lo vuelva a habilitar, seguirá sin ser de confianza a menos que use WITH CHECK . En otras palabras, si desea reafirmar su "confiabilidad", debe especificarlo explícitamente.

En otras palabras:

  • Cuando usas WITH NOCHECK , la restricción seguirá sin ser de confianza.
  • Cuando usas WITH CHECK se volverá confiable, pero solo si todos los datos existentes se ajustan a la restricción. Si algún dato existente viola la restricción, entonces la restricción no se habilitará y recibirá un mensaje de error.

Por supuesto, cuando digo "todos los datos existentes" solo me refiero a los datos a los que se aplica la restricción.

Puede haber escenarios en los que deshabilitó intencionalmente una restricción porque tuvo que ingresar datos que violan la restricción. En tales casos, si los datos no válidos deben permanecer en la base de datos, deberá usar WITH NOCHECK si desea volver a habilitar la restricción. Esto le permitirá habilitar la restricción sin que ningún dato existente se interponga.

A continuación hay ejemplos que demuestran esto.

Ejemplo 1:revisar las restricciones CHECK

Primero, usemos sys.check_constraints para echar un vistazo a todos CHECK restricciones en la base de datos actual.

SELECT 
  name,
  is_disabled,
  is_not_trusted,
  definition
FROM sys.check_constraints;

Resultado:

+-----------------+---------------+------------------+----------------------------------------+
| name            | is_disabled   | is_not_trusted   | definition                             |
|-----------------+---------------+------------------+----------------------------------------|
| chkPrice        | 0             | 0                | ([Price]>(0))                          |
| chkValidEndDate | 0             | 0                | ([EndDate]>=[StartDate])               |
| chkTeamSize     | 0             | 0                | ([TeamSize]>=(5) AND [TeamSize]<=(20)) |
| chkJobTitle     | 0             | 0                | ([JobTitle]<>'Digital Nomad')          |
+-----------------+---------------+------------------+----------------------------------------+

Podemos ver que todos están habilitados y son confiables (porque todos tienen ceros en is_disabled y no_es_de_confianza columnas).

Para este artículo, deshabilitaré y volveré a habilitar chkJobTitle restricción.

Ejemplo 2:deshabilitar la restricción

Aquí, deshabilito el chkJobTitle restricción:

ALTER TABLE Occupation  
NOCHECK CONSTRAINT chkJobTitle; 

Listo.

Ahora revisemos todas las restricciones nuevamente:

SELECT 
  name,
  is_disabled,
  is_not_trusted,
  definition
FROM sys.check_constraints;

Resultado:

+-----------------+---------------+------------------+----------------------------------------+
| name            | is_disabled   | is_not_trusted   | definition                             |
|-----------------+---------------+------------------+----------------------------------------|
| chkPrice        | 0             | 0                | ([Price]>(0))                          |
| chkValidEndDate | 0             | 0                | ([EndDate]>=[StartDate])               |
| chkTeamSize     | 0             | 0                | ([TeamSize]>=(5) AND [TeamSize]<=(20)) |
| chkJobTitle     | 1             | 1                | ([JobTitle]<>'Digital Nomad')          |
+-----------------+---------------+------------------+----------------------------------------+

Podemos ver que ha sido deshabilitado (porque is_disabled la columna se establece en 1 ).

Puede notar que is_not_trusted la columna también se establece en 1 . Esto indica que el CHECK el sistema no ha verificado la restricción para todas las filas.

Como se mencionó, un CHECK solo se puede confiar en la restricción si todos los datos han superado con éxito las condiciones de la restricción. Cuando deshabilitamos una restricción, esto abre la posibilidad de que datos no válidos ingresen a la base de datos. Por lo tanto, no podemos estar 100% seguros de que todos los datos sean válidos, por lo que la restricción se marca como no confiable.

La forma de garantizar que la restricción sea confiable nuevamente es volver a habilitarla usando WITH CHECK argumento. Esto hará que la restricción verifique todos los datos antes de que se vuelva a habilitar. Si algún dato no es válido, no se podrá volver a habilitar. Deberá actualizar los datos para que sean válidos o volver a habilitar la restricción mediante WITH NOCHECK argumento en su lugar (lo que hará que la restricción no sea de confianza).

Ejemplo 3:habilite la restricción usando la configuración predeterminada (SIN COMPROBACIÓN)

Volvamos a habilitar la restricción y ejecutemos la consulta nuevamente.

Para habilitar la restricción, seré perezoso y usaré la configuración predeterminada:

ALTER TABLE Occupation  
CHECK CONSTRAINT chkJobTitle; 

Ahora verifica el cambio:

SELECT 
  name,
  is_disabled,
  is_not_trusted,
  definition
FROM sys.check_constraints;

Resultado:

+-----------------+---------------+------------------+----------------------------------------+
| name            | is_disabled   | is_not_trusted   | definition                             |
|-----------------+---------------+------------------+----------------------------------------|
| chkPrice        | 0             | 0                | ([Price]>(0))                          |
| chkValidEndDate | 0             | 0                | ([EndDate]>=[StartDate])               |
| chkTeamSize     | 0             | 0                | ([TeamSize]>=(5) AND [TeamSize]<=(20)) |
| chkJobTitle     | 0             | 1                | ([JobTitle]<>'Digital Nomad')          |
+-----------------+---------------+------------------+----------------------------------------+

¿Viste lo que acaba de pasar? Aunque volví a habilitar la restricción, todavía no es de confianza.

Esto se debe a que fui perezoso (o tal vez simplemente olvidadizo) cuando habilité la restricción. Cuando habilité la restricción, olvidé especificar WITH CHECK . El valor predeterminado es WITH NOCHECK lo que significa que los datos existentes no se comprueban al volver a habilitar la restricción.

Esta es la razón por la que definitivamente debe saber lo que está haciendo al habilitar CHECK (y FOREIGN KEY ) restricciones. Al ser perezosos y no especificar explícitamente una configuración potencialmente importante, le damos permiso a SQL Server para hacer la vista gorda ante cualquier problema con los datos existentes.

Sin embargo, si la única razón por la que necesita deshabilitar la restricción es para insertar datos que violan la restricción, entonces el valor predeterminado WITH NOCHECK es probablemente lo que quieres.

Por cierto, para nuevas restricciones, el valor predeterminado es WITH CHECK .

Pero en mi caso, no inserté ni actualicé ninguna datos después de deshabilitar la restricción, por lo que si antes era confiable, debería seguir siéndolo ahora.

Entonces, ¿cómo puedo volver a confiar en mi restricción?

Ejemplo 4:habilite la restricción usando WITH CHECK

Si quiero que se vuelva a confiar en mi restricción, necesito especificar explícitamente WITH CHECK al volver a habilitarlo.

Desactivemos la restricción nuevamente:

ALTER TABLE Occupation  
NOCHECK CONSTRAINT chkJobTitle; 

Así que ahora vuelvo a donde estaba antes de volver a habilitarlo.

Lo que debería haber hecho cuando volví a habilitarlo fue esto:

ALTER TABLE Occupation  
WITH CHECK CHECK CONSTRAINT chkJobTitle; 

Ahora echa otro vistazo a la restricción:

SELECT 
  name,
  is_disabled,
  is_not_trusted,
  definition
FROM sys.check_constraints;

Resultado:

+-----------------+---------------+------------------+----------------------------------------+
| name            | is_disabled   | is_not_trusted   | definition                             |
|-----------------+---------------+------------------+----------------------------------------|
| chkPrice        | 0             | 0                | ([Price]>(0))                          |
| chkValidEndDate | 0             | 0                | ([EndDate]>=[StartDate])               |
| chkTeamSize     | 0             | 0                | ([TeamSize]>=(5) AND [TeamSize]<=(20)) |
| chkJobTitle     | 0             | 0                | ([JobTitle]<>'Digital Nomad')          |
+-----------------+---------------+------------------+----------------------------------------+

¡Uf! Mi restricción es confiable una vez más.

Ejemplo 5:habilite la restricción CHECK con datos no válidos

Por supuesto, solo se vuelve a confiar en mi restricción porque no inserté datos no válidos mientras estaba deshabilitada. Si hubiera hecho esto, no podría habilitarlo usando WITH CHECK , como se demuestra a continuación.

Si lo deshabilito de nuevo:

ALTER TABLE Occupation  
NOCHECK CONSTRAINT chkJobTitle; 

Ahora inserte datos no válidos (y devuelva los resultados):

INSERT INTO Occupation
VALUES ( 7, 'Digital Nomad' );

SELECT 
  OccupationId,
  JobTitle
FROM Occupation;

Resultado:

+----------------+-----------------+
| OccupationId   | JobTitle        |
|----------------+-----------------|
| 1              | Engineer        |
| 2              | Accountant      |
| 3              | Cleaner         |
| 4              | Attorney        |
| 5              | Sales Executive |
| 6              | Uber Driver     |
| 7              | Digital Nomad   |
+----------------+-----------------+

Así que insertamos con éxito datos no válidos (última fila).

Esto no es válido porque la definición de restricción es la siguiente:([JobTitle]<>'Digital Nomad')

Esto significa que el JobTitle la columna no debe contener el texto Digital Nomad .

Ahora intentemos volver a habilitar el CHECK restricción usando WITH CHECK y ver qué pasa.

ALTER TABLE Occupation  
WITH CHECK CHECK CONSTRAINT chkJobTitle; 

Resultado:

Msg 547, Level 16, State 0, Line 1
The ALTER TABLE statement conflicted with the CHECK constraint "chkJobTitle". The conflict occurred in database "Test", table "dbo.Occupation", column 'JobTitle'.

Entonces no podemos volver a habilitar la restricción usando WITH CHECK mientras tenemos datos en la tabla que violan el CHECK restricción. O necesitamos actualizar los datos o necesitamos usar WITH NOCHECK (o simplemente omítalo por completo).

Intentémoslo de nuevo usando WITH NOCHECK .

ALTER TABLE Occupation  
WITH NOCHECK CHECK CONSTRAINT chkJobTitle; 

Resultado:

Commands completed successfully.
Total execution time: 00:00:00.015

Entonces podemos habilitar con éxito la restricción si no verificamos los datos existentes.

Por supuesto, en este caso el CHECK la restricción aún no es de confianza. Si queremos que se confíe en la restricción, necesitaremos actualizar los datos para que no violen la restricción.

Ejemplo:

UPDATE Occupation
SET JobTitle = 'Unemployed'
WHERE OccupationId = 7;

SELECT 
  OccupationId,
  JobTitle
FROM Occupation;

Resultado:

+----------------+-----------------+
| OccupationId   | JobTitle        |
|----------------+-----------------|
| 1              | Engineer        |
| 2              | Accountant      |
| 3              | Cleaner         |
| 4              | Attorney        |
| 5              | Sales Executive |
| 6              | Uber Driver     |
| 7              | Unemployed      |
+----------------+-----------------+

Ahora podemos modificar el CHECK restricción para volver a ser confiable.

Hagamos los tres juntos:

ALTER TABLE Occupation  
NOCHECK CONSTRAINT chkJobTitle; 

ALTER TABLE Occupation  
WITH CHECK CHECK CONSTRAINT chkJobTitle; 

SELECT 
  name,
  is_disabled,
  is_not_trusted,
  definition
FROM sys.check_constraints;

Resultado:

+-----------------+---------------+------------------+----------------------------------------+
| name            | is_disabled   | is_not_trusted   | definition                             |
|-----------------+---------------+------------------+----------------------------------------|
| chkPrice        | 0             | 0                | ([Price]>(0))                          |
| chkValidEndDate | 0             | 0                | ([EndDate]>=[StartDate])               |
| chkTeamSize     | 0             | 0                | ([TeamSize]>=(5) AND [TeamSize]<=(20)) |
| chkJobTitle     | 0             | 0                | ([JobTitle]<>'Digital Nomad')          |
+-----------------+---------------+------------------+----------------------------------------+

¡Así que ahora nuestra restricción está habilitada y es confiable una vez más, y nuestra base de datos está libre de nómadas digitales!