Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

O que você deve saber sobre WITH NOCHECK ao habilitar uma restrição CHECK no SQL Server


Se você se encontrar na situação em que precisa reativar um CHECK restrição que foi anteriormente desabilitada, você definitivamente deve se certificar de que sabe o que está fazendo.

Em particular, você deve entender a diferença entre WITH NOCHECK e WITH CHECK argumentos.

Esses argumentos podem ser usados ​​no momento em que você habilita a restrição. Eles especificam se os dados existentes são validados ou não em relação ao seu CHECK reativado (ou adicionado recentemente) limitação. Basicamente, você tem a opção de verificar todos os dados existentes para quaisquer violações contra a restrição. Se você não especificar nada, os dados existentes não ser verificado. Por isso é importante entender como funciona.

A propósito, esses argumentos também se aplicam a restrições de chave estrangeira.



Como você pode esperar, WITH CHECK especifica que os dados existentes são validados e WITH NOCHECK especifica que não. O padrão é WITH NOCHECK .

Se você usar WITH NOCHECK , a restrição será sinalizada como não confiável. Na verdade, ele é sinalizado como não confiável quando você desativa a restrição. Mas quando você reativá-lo, ele permanecerá não confiável, a menos que você use WITH CHECK . Em outras palavras, se você deseja reafirmar sua “confiabilidade”, você deve especificar isso explicitamente.

Em outras palavras:
  • Quando você usa WITH NOCHECK , a restrição permanecerá não confiável.
  • Quando você usa WITH CHECK ele se tornará confiável, mas somente se todos os dados existentes estiverem em conformidade com a restrição. Se algum dado existente violar a restrição, a restrição não será habilitada e você receberá uma mensagem de erro.

Claro, quando digo “todos os dados existentes” estou me referindo apenas aos dados aos quais a restrição se aplica.

Pode haver cenários em que você desativou intencionalmente uma restrição porque teve que inserir dados que violam a restrição. Nesses casos, se os dados inválidos devem permanecer no banco de dados, você precisará usar WITH NOCHECK se você quiser reativar a restrição. Isso permitirá que você habilite a restrição sem que nenhum dado existente atrapalhe.

Abaixo estão exemplos que demonstram isso.

Exemplo 1 – Revisar restrições de VERIFICAÇÃO


Primeiro, vamos usar as sys.check_constraints para dar uma olhada em todos os CHECK restrições no banco de dados atual.
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ão habilitados e confiáveis ​​(porque todos têm zeros no is_disabled e is_not_trusted colunas).

Para este artigo, vou desativar e reativar o chkJobTitle limitação.

Exemplo 2 – Desativar a restrição


Aqui, desabilito o chkJobTitle limitação:
ALTER TABLE Occupation  
NOCHECK CONSTRAINT chkJobTitle; 

Feito.

Agora vamos revisar todas as restrições novamente:
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 ele foi desativado (porque seu is_disabled coluna está definida como 1 ).

Você pode notar que o is_not_trusted coluna também está definida como 1 . Isso indica que o CHECK restrição não foi verificada pelo sistema para todas as linhas.

Como mencionado, um CHECK A restrição só pode ser confiável se todos os dados tiverem passado com sucesso nas condições da restrição. Quando desabilitamos uma restrição, isso abre a possibilidade de dados inválidos entrarem no banco de dados. Portanto, não podemos ter 100% de certeza de que todos os dados são válidos, portanto, a restrição está sendo sinalizada como não confiável.

A maneira de garantir que a restrição seja confiável novamente é reativá-la usando o WITH CHECK argumento. Isso fará com que a restrição verifique todos os dados antes de ser reativada. Se algum dado for inválido, ele não poderá ser reativado. Você precisará atualizar os dados para que sejam válidos ou reativar a restrição usando o WITH NOCHECK argumento (o que fará com que a restrição permaneça não confiável).

Exemplo 3 – Habilite a restrição usando as configurações padrão (COM NOCHECK)


Vamos reativar a restrição e executar a consulta novamente.

Para habilitar a restrição, serei preguiçoso e usarei as configurações padrão:
ALTER TABLE Occupation  
CHECK CONSTRAINT chkJobTitle; 

Agora verifique a alteração:
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')          |
+-----------------+---------------+------------------+----------------------------------------+

Você viu o que acabou de acontecer? Mesmo que eu tenha habilitado a restrição novamente, ela ainda não é confiável.

Isso ocorre porque eu era preguiçoso (ou talvez apenas esquecido) quando habilitei a restrição. Quando habilitei a restrição, esqueci de especificar WITH CHECK . O padrão é WITH NOCHECK o que significa que os dados existentes não são verificados ao reativar a restrição.

É por isso que você definitivamente deve saber o que está fazendo ao ativar CHECK (e FOREIGN KEY ) restrições. Por ser preguiçoso e não especificar explicitamente uma configuração potencialmente importante, damos permissão ao SQL Server para fechar os olhos a quaisquer problemas com dados existentes.

No entanto, se todo o motivo pelo qual você precisava desabilitar a restrição é inserir dados que violam a restrição, então o padrão WITH NOCHECK é provavelmente o que você quer.

A propósito, para novas restrições, o padrão é WITH CHECK .

Mas, no meu caso, não inseri ou atualizei nenhum dados após desabilitar a restrição, portanto, se era confiável antes, ainda deve ser confiável agora.

Então, como posso obter minha restrição confiável novamente?

Exemplo 4 – Habilite a restrição usando WITH CHECK


Se eu quiser que minha restrição seja confiável novamente, preciso especificar explicitamente WITH CHECK ao reativá-lo.

Vamos desabilitar a restrição novamente:
ALTER TABLE Occupation  
NOCHECK CONSTRAINT chkJobTitle; 

Então agora estou de volta para onde estava antes de reativar.

O que eu deveria ter feito quando reativei foi isso:
ALTER TABLE Occupation  
WITH CHECK CHECK CONSTRAINT chkJobTitle; 

Agora dê outra olhada na restrição:
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')          |
+-----------------+---------------+------------------+----------------------------------------+

Ufa! Minha restrição é confiável mais uma vez.

Exemplo 5 – Habilitar a restrição CHECK com dados inválidos


Claro, minha restrição só é confiável novamente porque não inseri dados inválidos enquanto ela estava desabilitada. Se eu tivesse feito isso, não seria capaz de habilitá-lo usando WITH CHECK , conforme demonstrado abaixo.

Se eu desabilitar novamente:
ALTER TABLE Occupation  
NOCHECK CONSTRAINT chkJobTitle; 

Agora insira dados inválidos (e retorne os 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   |
+----------------+-----------------+

Então, inserimos com sucesso dados inválidos (última linha).

Isso é inválido porque a definição de restrição é a seguinte:([JobTitle]<>'Digital Nomad')

Isso significa que o JobTitle a coluna não deve conter o texto Digital Nomad .

Agora vamos tentar reativar o CHECK restrição usando WITH CHECK e veja o que acontece.
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'.

Portanto, não podemos reativar a restrição usando WITH CHECK enquanto temos dados na tabela que violam o CHECK limitação. Ou precisamos atualizar os dados ou precisamos usar WITH NOCHECK (ou simplesmente omiti-lo completamente).

Vamos tentar novamente usando WITH NOCHECK .
ALTER TABLE Occupation  
WITH NOCHECK CHECK CONSTRAINT chkJobTitle; 

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

Assim, podemos habilitar com sucesso a restrição se não verificarmos os dados existentes.

Claro, neste caso o CHECK restrição ainda não é confiável. Se quisermos que a restrição seja confiável, precisaremos atualizar os dados para que não viole a restrição.

Exemplo:
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      |
+----------------+-----------------+

Agora podemos alterar o CHECK restrição para se tornar confiável novamente.

Vamos fazer os três 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')          |
+-----------------+---------------+------------------+----------------------------------------+

Então agora nossa restrição está habilitada e confiável mais uma vez, e nosso banco de dados está livre de nômades digitais!