Ако някога се окажете в ситуация, в която трябва да активирате отново CHECK
ограничение, което преди е било деактивирано, определено трябва да се уверите, че знаете какво правите.
По-специално, трябва да разберете разликата между WITH NOCHECK
и WITH CHECK
аргументи.
Тези аргументи могат да се използват в момента, в който активирате ограничението. Те уточняват дали съществуващите данни се потвърждават или не спрямо повторно активираните (или новодобавени) CHECK
ограничение. По принцип имате възможност да проверите всички съществуващи данни за нарушения на ограничението. Ако не посочите нищо, съществуващите данни няма бъде проверен. Ето защо е важно да разберете как работи.
Между другото, тези аргументи се отнасят и за ограниченията на външния ключ.
Както може да очаквате, WITH CHECK
указва, че съществуващите данни са валидирани и WITH NOCHECK
уточнява, че не е така. По подразбиране е WITH NOCHECK
.
Ако използвате WITH NOCHECK
, ограничението ще бъде маркирано като ненадеждно. Всъщност той е маркиран като ненадежден, когато деактивирате ограничението. Но когато го активирате отново, той ще остане ненадежден, освен ако не използвате WITH CHECK
. С други думи, ако искате да повторите неговата „надеждност“, трябва изрично да посочите това.
С други думи:
- Когато използвате
WITH NOCHECK
, ограничението ще остане ненадеждно. - Когато използвате
WITH CHECK
ще стане доверен, но само ако всички съществуващи данни отговарят на ограничението. Ако някакви съществуващи данни нарушават ограничението, ограничението няма да бъде активирано и ще получите съобщение за грешка.
Разбира се, когато казвам „всички съществуващи данни“, имам предвид само данни, за които се прилага ограничението.
Възможно е да има сценарии, при които умишлено сте деактивирали ограничение, защото е трябвало да въведете данни, които нарушават ограничението. В такива случаи, ако невалидните данни трябва да останат в базата данни, ще трябва да използвате WITH NOCHECK
ако искате да активирате отново ограничението. Това ще ви позволи да активирате ограничението, без никакви съществуващи данни да пречат.
По-долу са дадени примери, които демонстрират това.
Пример 1 – Преглед на ограниченията CHECK
Първо, нека използваме sys.check_constraints
за да разгледате всички CHECK
ограничения в текущата база данни.
SELECT name, is_disabled, is_not_trusted, definition FROM sys.check_constraints;
Резултат:
+-----------------+---------------+------------------+----------------------------------------+ | 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') | +-----------------+---------------+------------------+----------------------------------------+
Можем да видим, че всички те са активирани и надеждни (защото всички имат нули в is_disabled и не_доверен колони).
За тази статия ще деактивирам и активирам отново chkJobTitle ограничение.
Пример 2 – Деактивиране на ограничението
Тук деактивирам chkJobTitle ограничение:
ALTER TABLE Occupation NOCHECK CONSTRAINT chkJobTitle;
Готово.
Сега нека прегледаме отново всички ограничения:
SELECT name, is_disabled, is_not_trusted, definition FROM sys.check_constraints;
Резултат:
+-----------------+---------------+------------------+----------------------------------------+ | 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') | +-----------------+---------------+------------------+----------------------------------------+
Можем да видим, че е деактивиран (защото is_disabled колоната е настроена на 1 ).
Може да забележите, че
is_not_trusted
колоната също е настроена на
1
. Това показва, че CHECK
ограничението не е проверено от системата за всички редове.
Както споменахме, CHECK
на ограничението може да се има доверие само ако всички данни са преминали успешно условията на ограничението. Когато деактивираме ограничение, това отваря потенциала невалидни данни да влязат в базата данни. Следователно не можем да бъдем 100% сигурни, че всички данни са валидни, поради което ограничението е маркирано като ненадеждно.
Начинът да гарантирате, че ограничението отново е надеждно, е да го активирате отново с помощта на WITH CHECK
аргумент. Това ще накара ограничението да провери всички данни, преди да бъде повторно активирано. Ако някои данни са невалидни, те няма да могат да бъдат активирани отново. Ще трябва или да актуализирате данните, така че да са валидни, или да активирате отново ограничението с помощта на WITH NOCHECK
вместо аргумент (което ще накара ограничението да остане ненадеждно).
Пример 3 – Активирайте ограничението, като използвате настройките по подразбиране (С NOCHECK)
Нека да активираме отново ограничението и да стартираме заявката отново.
За да активирам ограничението, ще бъда мързелив и ще използвам настройките по подразбиране:
ALTER TABLE Occupation CHECK CONSTRAINT chkJobTitle;
Сега проверете промяната:
SELECT name, is_disabled, is_not_trusted, definition FROM sys.check_constraints;
Резултат:
+-----------------+---------------+------------------+----------------------------------------+ | 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') | +-----------------+---------------+------------------+----------------------------------------+
Видяхте ли какво се случи току-що? Въпреки че отново активирах ограничението, то все още не е надеждно.
Това е така, защото бях мързелив (или може би просто забравих), когато активирах ограничението. Когато активирах ограничението, забравих да посоча WITH CHECK
. По подразбиране е WITH NOCHECK
което означава, че съществуващите данни не се проверяват при повторното активиране на ограничението.
Ето защо определено трябва да знаете какво правите, когато активирате CHECK
(и FOREIGN KEY
) ограничения. Като сме мързеливи и не указваме изрично потенциално важна настройка, ние даваме разрешение на SQL Server да си затваря очите за всякакви проблеми със съществуващите данни.
Въпреки това, ако цялата причина, поради която трябва да деактивирате ограничението, е да вмъкнете данни, които нарушават ограничението, тогава WITH NOCHECK
по подразбиране вероятно е това, което искате.
Между другото, за новите ограничения по подразбиране е WITH CHECK
.
Но в моя случай не вмъкнах или актуализирах никоя данни след деактивиране на ограничението, така че ако преди е било надеждно, сега трябва да е надеждно.
И така, как мога да получа отново доверие на моето ограничение?
Пример 4 – Активирайте ограничението с помощта на WITH CHECK
Ако искам отново да има доверие на моето ограничение, трябва изрично да посоча WITH CHECK
когато го активирате отново.
Нека отново деактивираме ограничението:
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;
Резултат:
+-----------------+---------------+------------------+----------------------------------------+ | 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') | +-----------------+---------------+------------------+----------------------------------------+
фу! Моето ограничение отново е надеждно.
Пример 5 – Активиране на ограничението CHECK с невалидни данни
Разбира се, моето ограничение отново се доверява само, защото не вмъкнах невалидни данни, докато беше деактивирано. Ако бях направил това, нямаше да мога да го активирам с помощта на WITH CHECK
, както е показано по-долу.
Ако го деактивирам отново:
ALTER TABLE Occupation NOCHECK CONSTRAINT chkJobTitle;
Сега поставете невалидни данни (и върнете резултатите):
INSERT INTO Occupation VALUES ( 7, 'Digital Nomad' ); SELECT OccupationId, JobTitle FROM Occupation;
Резултат:
+----------------+-----------------+ | OccupationId | JobTitle | |----------------+-----------------| | 1 | Engineer | | 2 | Accountant | | 3 | Cleaner | | 4 | Attorney | | 5 | Sales Executive | | 6 | Uber Driver | | 7 | Digital Nomad | +----------------+-----------------+
Така че успешно вмъкнахме невалидни данни (последен ред).
Това е невалидно, тъй като дефиницията на ограничението е както следва:([JobTitle]<>'Digital Nomad')
Това означава, че
JobTitle
колоната не трябва да съдържа текста Digital Nomad
.
Сега нека се опитаме да активираме отново CHECK
ограничение с помощта на WITH CHECK
и вижте какво ще се случи.
ALTER TABLE Occupation WITH CHECK CHECK CONSTRAINT chkJobTitle;
Резултат:
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'.
Така че не можем да активираме отново ограничението с помощта на WITH CHECK
докато имаме данни в таблицата, които нарушават CHECK
ограничение. Или трябва да актуализираме данните, или трябва да използваме WITH NOCHECK
(или просто го пропуснете напълно).
Нека опитаме отново с WITH NOCHECK
.
ALTER TABLE Occupation WITH NOCHECK CHECK CONSTRAINT chkJobTitle;
Резултат:
Commands completed successfully. Total execution time: 00:00:00.015
Така че можем успешно да активираме ограничението, ако не проверим съществуващите данни.
Разбира се, в този случай CHECK
на ограничението все още не се вярва. Ако искаме ограничението да бъде надеждно, ще трябва да актуализираме данните, така че да не нарушават ограничението.
Пример:
UPDATE Occupation SET JobTitle = 'Unemployed' WHERE OccupationId = 7; SELECT OccupationId, JobTitle FROM Occupation;
Резултат:
+----------------+-----------------+ | OccupationId | JobTitle | |----------------+-----------------| | 1 | Engineer | | 2 | Accountant | | 3 | Cleaner | | 4 | Attorney | | 5 | Sales Executive | | 6 | Uber Driver | | 7 | Unemployed | +----------------+-----------------+
Сега можем да променим CHECK
ограничение да станете отново доверени.
Нека направим и трите заедно:
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;
Резултат:
+-----------------+---------------+------------------+----------------------------------------+ | 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') | +-----------------+---------------+------------------+----------------------------------------+
Така че сега нашето ограничение отново е активирано и надеждно, а нашата база данни е свободна от цифрови номади!