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!