Szybko zmień kolumnę NULL na NOT NULL

11

Mam tabelę z milionami wierszy i kolumnę, która dopuszcza wartości NULL. Jednak żaden wiersz nie ma obecnie wartości NULL dla tej kolumny (mogę to dość szybko zweryfikować za pomocą zapytania). Jednak gdy wykonam polecenie

ALTER TABLE MyTable ALTER COLUMN MyColumn BIGINT NOT NULL;

zapytanie trwa wiecznie relatywnie rzecz biorąc. W rzeczywistości zajmuje to od 10 do 20 minut, ponad dwa razy dłużej niż dodanie ograniczenia sprawdzania. Czy istnieje sposób na natychmiastową aktualizację metadanych tabeli dla tej kolumny, zwłaszcza że wiem, że żaden wiersz nie ma wartości NULL dla tej kolumny?

Joseph Daigle
źródło
2
Nie (lub przynajmniej nie stosując udokumentowanych / obsługiwanych metod). Zobacz, dlaczego ZMIEŃ KOLUMNĘ, aby NIE ZEROWAĆ, powoduje ogromny wzrost pliku dziennika?
Martin Smith
2
Może również oczekiwać na Sch-Mblokadę, gdy zajmie to „na zawsze”. Czy chciałeś sprawdzić, czy czeka, czy jest zajęty?
Martin Smith
@MartinSmith Wyjaśniłem, co mam na myśli przez wieczność . Testuję to w środowisku deweloperskim, gdzie żadna inna sesja nie trafia do bazy danych. W końcu się kończy. Ale odpowiedź, którą połączyłeś, wyjaśnia, dlaczego to trwa tak długo. Jeśli możesz przeredagować swój komentarz jako odpowiedź, wtedy go zaakceptuję.
Joseph Daigle,

Odpowiedzi:

12

Odpowiedź @ ypercube radzi sobie z tym częściowo, ponieważ zmienia się tylko metadane.

Dodanie ograniczenia NOCHECKoznacza, że ​​żadne wiersze nie będą musiały być czytane, aby je zweryfikować, a jeśli zaczynasz od pozycji, w której kolumna nie zawiera NULLwartości (i jeśli wiesz, że żaden nie zostanie dodany między sprawdzaniem a dodawaniem ograniczenia), ponieważ ograniczenie uniemożliwia tworzenie NULLwartości z przyszłości INSERTlub UPDATEoperacji, to zadziała.

Dodanie ograniczenia może jednak mieć wpływ na współbieżne transakcje. ALTER TABLEBędą musiały nabyć Sch-Mblokadę pierwszy. Podczas oczekiwania na to wszystkie inne dostęp do tabeli zostaną zablokowane, jak opisano tutaj .

Po uzyskaniu Sch-Mblokady operacja powinna być jednak dość szybka.

Jednym z problemów jest to, że nawet jeśli wiesz, że kolumna w rzeczywistości nie ma żadnych NULLograniczeń, optymalizator zapytań nie ufa temu ograniczeniu, co oznacza, że ​​plany mogą być nieoptymalne.

CREATE TABLE T (X INT NULL)

INSERT INTO T 
SELECT ROW_NUMBER() OVER (ORDER BY @@SPID)
FROM master..spt_values

ALTER TABLE T WITH NOCHECK
  ADD  CONSTRAINT X_NOT_NULL 
    CHECK (X IS NOT NULL) ; 

SELECT *
FROM T 
WHERE X NOT IN (SELECT X FROM T)

Plan

Porównaj to z prostszym

ALTER TABLE T ALTER COLUMN X INT NOT NULL

SELECT *
FROM T 
WHERE X NOT IN (SELECT X FROM T)

Plan

Jednym z możliwych problemów, które możesz napotkać podczas zmiany definicji kolumny w ten sposób, jest to, że musi on nie tylko odczytać wszystkie wiersze, aby sprawdzić, czy spełniają warunek, ale może także faktycznie wykonać zarejestrowane aktualizacje wierszy .

Możliwym rozwiązaniem w połowie drogi może być dodanie ograniczenia sprawdzania WITH CHECK. Będzie to wolniejsze niż WITH NOCHECKw przypadku konieczności odczytu wszystkich wierszy, ale pozwala optymalizatorowi zapytań podać prostszy plan w powyższym zapytaniu i powinno unikać potencjalnego problemu z zalogowanymi aktualizacjami.

Martin Smith
źródło
7

Zamiast zmieniać kolumnę można dodać CHECKograniczenie tabeli za pomocą NOCHECKopcji:

ALTER TABLE MyTable WITH NOCHECK
  ADD  CONSTRAINT MyColumn_NOT_NULL 
    CHECK (MyColumn IS NOT NULL) ;
ypercubeᵀᴹ
źródło
1
Zapobiegnie to przyszłym aktualizacjom lub wstawkom, które utworzą kolumnę, NULLale nie będą mogły być używane przez optymalizator zapytań.
Martin Smith
@MartinSmith O tak, właśnie przeczytałem odpowiedź i komentarze w podobnym pytaniu: Jak dodać kolumnę NOT NULL do dużej tabeli w SQL Server? Dodaj odpowiedź z problemami lub lepsze rozwiązanie, a ja usunę moje.
ypercubeᵀᴹ
2
Nie mam lepszego rozwiązania. Głosowałem za tym, ponieważ zapewnia on częściowe rozwiązanie. Jeśli wszystko, co OP chce zrobić, to zapobiec nieprawidłowym danym, zadziała (i powinno to być szybsze niż ALTER COLUMNpo uzyskaniu Sch-Mblokady, nie trzeba w ogóle skanować wierszy). Po prostu wskazując, że nie jest tak samo (np. Jeśli zostanie użyty w NOT INzapytaniu, plan będzie bardziej złożony)
Martin Smith,