Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Cosa dovresti sapere CON NOCHECK quando si abilita un vincolo CHECK in SQL Server

Se mai ti trovi nella situazione in cui devi riattivare un CHECK vincolo che è stato precedentemente disabilitato, dovresti assolutamente assicurarti di sapere cosa stai facendo.

In particolare, dovresti capire la differenza tra WITH NOCHECK e WITH CHECK argomenti.

Questi argomenti possono essere utilizzati nel momento in cui si abilita il vincolo. Specificano se i dati esistenti vengono convalidati o meno rispetto al tuo CHECK riattivato (o appena aggiunto) vincolo. Fondamentalmente hai la possibilità di controllare tutti i dati esistenti per eventuali violazioni rispetto al vincolo. Se non specifichi nulla, i dati esistenti non lo saranno essere controllato. Ecco perché è importante capire come funziona.

A proposito, questi argomenti si applicano anche ai vincoli di chiave esterna.

Come puoi immaginare, WITH CHECK specifica che i dati esistenti sono convalidati e WITH NOCHECK specifica che non lo è. L'impostazione predefinita è WITH NOCHECK .

Se usi WITH NOCHECK , il vincolo verrà contrassegnato come non attendibile. In realtà, viene contrassegnato come non attendibile quando si disabilita il vincolo. Ma quando lo riattivi, rimarrà non attendibile a meno che non usi WITH CHECK . In altre parole, se vuoi riaffermare la sua “affidabilità”, devi specificarlo esplicitamente.

In altre parole:

  • Quando usi WITH NOCHECK , il vincolo rimarrà non attendibile.
  • Quando usi WITH CHECK diventerà affidabile, ma solo se tutti i dati esistenti sono conformi al vincolo. Se i dati esistenti violano il vincolo, il vincolo non verrà abilitato e riceverai un messaggio di errore.

Ovviamente, quando dico "tutti i dati esistenti" mi riferisco solo ai dati a cui si applica il vincolo.

Potrebbero esserci scenari in cui hai intenzionalmente disabilitato un vincolo perché hai dovuto inserire dati che violano il vincolo. In questi casi, se i dati non validi devono rimanere nel database, dovrai utilizzare WITH NOCHECK se si desidera riattivare il vincolo. Ciò ti consentirà di abilitare il vincolo senza che i dati esistenti si intromettano.

Di seguito sono riportati esempi che lo dimostrano.

Esempio 1 – Rivedere i vincoli CHECK

Per prima cosa, utilizziamo sys.check_constraints per dare un'occhiata a tutti i CHECK vincoli nel database corrente.

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

Risultato:

+-----------------+---------------+------------------+----------------------------------------+
| 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')          |
+-----------------+---------------+------------------+----------------------------------------+

Possiamo vedere che sono tutti abilitati e affidabili (perché hanno tutti zero in is_disabled e non è attendibile colonne).

Per questo articolo, disabiliterò e riattiverò chkJobTitle vincolo.

Esempio 2:disabilitare il vincolo

Qui, disabilito chkJobTitle vincolo:

ALTER TABLE Occupation  
NOCHECK CONSTRAINT chkJobTitle; 

Fatto.

Ora esaminiamo di nuovo tutti i vincoli:

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

Risultato:

+-----------------+---------------+------------------+----------------------------------------+
| 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')          |
+-----------------+---------------+------------------+----------------------------------------+

Possiamo vedere che è stato disabilitato (perché il suo is_disabled la colonna è impostata su 1 ).

Potresti notare che non è attendibile anche la colonna è impostata su 1 . Ciò indica che il CHECK il vincolo non è stato verificato dal sistema per tutte le righe.

Come accennato, un CHECK il vincolo può essere considerato attendibile solo se tutti i dati hanno superato con successo le condizioni del vincolo. Quando disabilitiamo un vincolo, questo apre la possibilità che dati non validi entrino nel database. Pertanto non possiamo essere certi al 100% che tutti i dati siano validi, quindi il vincolo viene contrassegnato come non attendibile.

Il modo per garantire che il vincolo sia nuovamente attendibile è riattivarlo utilizzando il WITH CHECK discussione. Ciò farà sì che il vincolo controlli tutti i dati prima di essere riattivato. Se alcuni dati non sono validi, non sarà possibile riattivarli. Dovrai aggiornare i dati in modo che siano validi o riattivare il vincolo utilizzando il WITH NOCHECK argomento invece (che farà sì che il vincolo rimanga non attendibile).

Esempio 3:abilitare il vincolo utilizzando le impostazioni predefinite (CON NOCHECK)

Riabilitiamo il vincolo ed eseguiamo di nuovo la query.

Per abilitare il vincolo, sarò pigro e userò le impostazioni predefinite:

ALTER TABLE Occupation  
CHECK CONSTRAINT chkJobTitle; 

Ora verifica la modifica:

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

Risultato:

+-----------------+---------------+------------------+----------------------------------------+
| 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')          |
+-----------------+---------------+------------------+----------------------------------------+

Hai visto cosa è appena successo? Anche se ho abilitato di nuovo il vincolo, non è ancora attendibile.

Questo perché ero pigro (o forse solo smemorato) quando ho abilitato il vincolo. Quando ho abilitato il vincolo, ho dimenticato di specificare WITH CHECK . L'impostazione predefinita è WITH NOCHECK il che significa che i dati esistenti non vengono controllati quando si riattiva il vincolo.

Questo è il motivo per cui dovresti assolutamente sapere cosa stai facendo quando abiliti CHECK (e FOREIGN KEY ) vincoli. Essendo pigri e non specificando esplicitamente un'impostazione potenzialmente importante, concediamo a SQL Server il permesso di chiudere un occhio su eventuali problemi con i dati esistenti.

Tuttavia, se l'intero motivo necessario per disabilitare il vincolo è inserire dati che violano il vincolo, il valore predefinito WITH NOCHECK è probabilmente quello che vuoi.

A proposito, per i nuovi vincoli, l'impostazione predefinita è WITH CHECK .

Ma nel mio caso, non ho inserito né aggiornato nessuno dati dopo aver disabilitato il vincolo, quindi se prima era affidabile, dovrebbe esserlo ancora adesso.

Quindi, come posso ritenere attendibile il mio vincolo?

Esempio 4:abilitare il vincolo utilizzando WITH CHECK

Se voglio che il mio vincolo sia nuovamente attendibile, devo specificare esplicitamente WITH CHECK alla riattivazione.

Disabilitiamo di nuovo il vincolo:

ALTER TABLE Occupation  
NOCHECK CONSTRAINT chkJobTitle; 

Quindi ora sono tornato dov'ero prima di riattivarlo.

Quello che avrei dovuto fare quando l'ho riattivato era questo:

ALTER TABLE Occupation  
WITH CHECK CHECK CONSTRAINT chkJobTitle; 

Ora dai un'altra occhiata al vincolo:

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

Risultato:

+-----------------+---------------+------------------+----------------------------------------+
| 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')          |
+-----------------+---------------+------------------+----------------------------------------+

Uff! Il mio vincolo è di nuovo affidabile.

Esempio 5:abilitare il vincolo CHECK con dati non validi

Ovviamente, il mio vincolo è di nuovo attendibile solo perché non ho inserito dati non validi mentre era disabilitato. Se l'avessi fatto, non sarei in grado di abilitarlo utilizzando WITH CHECK , come illustrato di seguito.

Se lo disabilito di nuovo:

ALTER TABLE Occupation  
NOCHECK CONSTRAINT chkJobTitle; 

Ora inserisci dati non validi (e restituisci i risultati):

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

SELECT 
  OccupationId,
  JobTitle
FROM Occupation;

Risultato:

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

Quindi abbiamo inserito correttamente dati non validi (ultima riga).

Questo non è valido perché la definizione del vincolo è la seguente:([JobTitle]<>'Digital Nomad')

Ciò significa che il JobTitle la colonna non deve contenere il testo Digital Nomad .

Ora proviamo a riattivare il CHECK vincolo usando WITH CHECK e guarda cosa succede.

ALTER TABLE Occupation  
WITH CHECK CHECK CONSTRAINT chkJobTitle; 

Risultato:

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'.

Quindi non possiamo riattivare il vincolo usando WITH CHECK mentre abbiamo dati nella tabella che violano il CHECK vincolo. O dobbiamo aggiornare i dati o dobbiamo usare WITH NOCHECK (o semplicemente ometterlo del tutto).

Riproviamo usando WITH NOCHECK .

ALTER TABLE Occupation  
WITH NOCHECK CHECK CONSTRAINT chkJobTitle; 

Risultato:

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

Quindi possiamo abilitare con successo il vincolo se non controlliamo i dati esistenti.

Naturalmente, in questo caso il CHECK vincolo non è ancora attendibile. Se vogliamo che il vincolo sia affidabile, dovremo aggiornare i dati in modo che non violi il vincolo.

Esempio:

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

SELECT 
  OccupationId,
  JobTitle
FROM Occupation;

Risultato:

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

Ora possiamo modificare il CHECK costrizione a diventare nuovamente attendibile.

Facciamo tutti e tre insieme:

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;

Risultato:

+-----------------+---------------+------------------+----------------------------------------+
| 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')          |
+-----------------+---------------+------------------+----------------------------------------+

Quindi ora il nostro vincolo è abilitato e affidabile ancora una volta e il nostro database è libero dai nomadi digitali!