[ Część 1 | Część 2 | Część 3 | Część 4 ]
W pierwszej części tej serii pokazałem, co dzieje się z fizyczną stroną po zmianie kolumny IDENTITY z int na bigint. Aby wszystko było proste, stworzyłem bardzo prostą stertę bez indeksów i ograniczeń. Niestety, większość z nas nie ma takiego luksusu – ważny stół, który musi się zmienić, ale nie można go po prostu odtworzyć od zera, prawdopodobnie ma wiele atrybutów stojących bezpośrednio na naszej drodze. W tym poście chciałem pokazać te bardziej popularne, nawet bez wchodzenia w egzotyczne rzeczy, takie jak OLTP w pamięci i Columnstore.
Klucz główny
Mam nadzieję, że wszystkie twoje tabele mają klucz podstawowy; jeśli jednak w grę wchodzi kolumna IDENTITY, nie będzie łatwo zmienić bazowy typ danych. Weź te proste przykłady, zarówno klastrowe, jak i nieklastrowe klucze podstawowe:
UTWÓRZ TABELĘ dbo.Test1(ID INT IDENTITY(1,1), OGRANICZENIE PK_1 KLUCZ PODSTAWOWY NIESKLASTRAROWANY (ID)); CREATE TABLE dbo.Test2( ID INT IDENTITY (1,1), CONSTRAINT PK_2 PRIMARY KEY CLASTERED (ID));
Jeśli spróbuję zmienić kolumnę:
ALTER TABLE dbo.Test1 ALTER COLUMN ID BIGINT; GOALTER TABLE dbo.Test2 ALTER COLUMN ID BIGINT;
Otrzymuję parę komunikatów o błędach dla każdej ALTER (pokazuje tylko pierwszą parę):
Msg 5074, Poziom 16, Stan 1Obiekt 'PK_1' jest zależny od kolumny 'ID'.
Msg 4922, Poziom 16, Stan 9
ALTER TABLE ALTER COLUMN ID nie powiodło się, ponieważ jeden lub więcej obiektów ma dostęp do tej kolumny.
Podsumowanie:będziemy musieli usunąć klucz podstawowy , niezależnie od tego, czy jest zgrupowany.
Indeksy
Najpierw weźmy kilka tabel jak powyżej, używając unikalnego indeksu zamiast klucza podstawowego:
CREATE TABLE dbo.Test3(ID INT IDENTITY(1,1), INDEX IX_3 UNIQUE NIEKLASTERY (ID)); CREATE TABLE dbo.Test4( ID INT IDENTITY(1,1), INDEX IX_4 UNIQUE CLUSTERED (ID) );
Uruchomienie podobnych poleceń ALTER powyżej prowadzi do tych samych komunikatów o błędach. Pozostaje to prawdą, nawet jeśli wyłączę indeksy:
ZMIEŃ INDEKS IX_3 NA dbo.Test3 WYŁĄCZ; INDEKS GOALTER IX_4 NA dbo.Test4 WYŁĄCZ;
Podobne wyniki dla różnych innych typów kombinacji indeksów, takich jak dołączona kolumna lub filtr:
CREATE TABLE dbo.Test5(ID INT IDENTITY(1,1), x CHAR(1));CREATE INDEX IX_5 ON dbo.Test5(x) INCLUDE(ID); CREATE TABLE dbo.Test6( ID INT IDENTITY(1,1), x CHAR(1));CREATE INDEX IX_6 ON dbo.Test6(x) WHERE ID> 0;
Podsumowanie:będziemy musieli usunąć i ponownie utworzyć wszystkie indeksy , klastrowane lub nie, które odwołują się do kolumny IDENTITY — w kluczu lub w INCLUDE. Jeśli kolumna IDENTITY jest częścią indeksu klastrowego, oznacza to wszystkie indeksy , ponieważ wszystkie z definicji odwołują się do klucza klastrowania. A samo ich wyłączenie nie wystarczy.
Kolumny obliczane
Chociaż powinno to być stosunkowo rzadkie, widziałem kolumny obliczone na podstawie kolumny IDENTITY. Na przykład:
CREATE TABLE dbo.Test7(ID INT IDENTITY(1,1), NextID AS (ID+1));
Tym razem, gdy próbujemy zmienić, otrzymujemy tę samą parę błędów, ale z nieco innym tekstem:
Msg 5074, Poziom 16, Stan 1Kolumna „NextID” jest zależna od kolumny „ID”.
Msg 4922, Level 16, State 9
ALTER TABLE ALTER COLUMN ID nie powiodło się, ponieważ jeden lub więcej obiektów ma dostęp do tej kolumny.
Dzieje się tak nawet wtedy, gdy zmienimy definicję kolumny wyliczanej w celu dopasowania do docelowego typu danych:
CREATE TABLE dbo.Test8(ID INT IDENTITY(1,1), NextID AS (CONVERT(BIGINT, ID) + 1));
Podsumowanie:będziemy musieli zmienić definicje kolumn obliczanych lub całkowicie je usunąć.
Wyświetlenia zindeksowane
Zindeksowane widoki pokazują również ich sprawiedliwy udział w użyciu. Skonstruujmy widok indeksowany, który nawet nie odwołuje się do kolumny IDENTITY (zwróć uwagę, że nie ma innych indeksów ani ograniczeń w tabeli bazowej):
CREATE TABLE dbo.Test9(ID INT IDENTITY(1,1), x CHAR(1));GO CREATE VIEW dbo.vTest9AWITH SCHEMABINDINGAS SELECT x, c =COUNT_BIG(*) FROM dbo.Test9 GROUP BY x;GO UTWÓRZ UNIKATOWY SKLASTROWANY INDEKS IX_9A NA dbo.vTest9A(x);
Jeszcze raz spróbujemy ALTER i tym razem to się uda . Przyznam się, że byłem tym zaskoczony, ponieważ funkcja SCHEMABINDING ma zapobiegać wszelkim zmianom w tabeli bazowej, ale w tym przypadku dotyczy tylko kolumn, do których wyraźnie odwołuje się widok. Jeśli stworzymy nieco inny widok:
UTWÓRZ WIDOK dbo.vTest9B Z POWIĄZANIAMI SCHEMATÓW JAKO SELECT ID, c =COUNT_BIG(*) Z dbo.Test9 GRUPUJ WEDŁUG ID;Teraz nie powiedzie się z powodu zależności kolumn:
Msg 5074, poziom 16, stan 1
Obiekt „vTest9B” jest zależny od kolumny „ID”.
Wiadomość 4922, poziom 16, stan 9
ALTER TABLE ALTER COLUMN ID nie powiodło się, ponieważ jeden lub więcej obiektów ma dostęp do tej kolumny.Podsumowanie:będziemy musieli usunąć wszystkie indeksy we wszystkich widokach, które wyraźnie odwołują się do kolumny IDENTITY , a także wszystkie indeksy w dowolnym widoku, który odwołuje się do kolumny IDENTITY w swoim indeksie klastrowym.
Przychodzące klucze obce
Prawdopodobnie najbardziej problematycznym aspektem kluczy podstawowych TOŻSAMOŚCI jest to, że ze względu na samą naturę surogatów chodzi o to, aby często używać tego klucza zastępczego w wielu powiązanych tabelach. Nie mam zamiaru opowiadać się za unikaniem uczciwości referencyjnej, ale w tym przypadku może to również trochę stanąć na naszej drodze. Wiemy z góry, że nie możemy zmienić kolumny, która jest częścią klucza podstawowego lub ograniczenia przez unikalność, a aby kolejna tabela wskazywała tutaj z ograniczeniem klucza obcego, musi istnieć jedna z tych dwóch rzeczy. Załóżmy więc, że mamy następujące dwie tabele:
CREATE TABLE dbo.TestParent(ID INT IDENTITY(1,1), CONSTRAINT PK_Parent PRIMARY KEY CLUSTERED(ID));GO CREATE TABLE dbo.TestChild( ParentID INT NOT NULL, OGRANICZENIE FK_Parent FOREIGN KEY (ParentID) REFERENCES dbo. TestParent(ID));Zanim będziemy mogli nawet rozważyć zmianę typu danych kolumny, musimy usunąć ograniczenie:
ZMIEŃ TABELĘ dbo.TestParent OGRANICZENIE UPUSZCZANIA PK_Parent;I oczywiście nie możemy, bez usunięcia ograniczenia klucza obcego, ponieważ powoduje to wyświetlenie następującego komunikatu o błędzie:
Komunikat 3725, poziom 16, stan 0
Do ograniczenia „PK_Parent” odwołuje się tabela „TestChild”, ograniczenie klucza obcego „FK_Parent”.
Komunikat 3727, poziom 16, stan 0
Może nie upuść ograniczenia. Zobacz poprzednie błędy.Ten błąd pozostaje, nawet jeśli najpierw wyłączymy ograniczenie klucza obcego:
ALTER TABELA dbo.TestChild NOCHECK CONSTRAINT FK_Parent;Oprócz tego należy pamiętać, że do zmiany typu danych będą potrzebne również kolumny z odwołaniami. Co więcej, te kolumny prawdopodobnie uczestniczą w niektórych z powyższych elementów, które mogą podobnie zapobiegać zmianom w tabelach podrzędnych. Aby wszystko było w pełni zgodne i zsynchronizowane, musimy:
- upuść odpowiednie ograniczenia i indeksy w tabeli nadrzędnej
- upuść odpowiednie ograniczenia klucza obcego w tabelach podrzędnych
- upuść wszystkie indeksy w tabelach podrzędnych, które odwołują się do kolumny FK (i zajmują się wszelkimi odpowiednimi kolumnami obliczeniowymi / widokami indeksowanymi)
- zmień typ danych w tabelach nadrzędnych i wszystkich tabelach podrzędnych
- odtwórz wszystko
Podsumowanie:będziemy musieli usunąć przychodzące klucze obce i potencjalnie będzie to miało cały szereg efektów kaskadowych. Samo wyłączenie kluczy obcych nie wystarczy i i tak nie byłoby trwałym rozwiązaniem, ponieważ typ danych będzie musiał się w końcu zmienić również w tabelach podrzędnych.
Wniosek
Wiem, że wygląda na to, że idziemy powoli, i przyznaję, że w tym poście wydaje mi się, że oddalam się od rozwiązania, a nie w jego kierunku. Dojdę do tego, jest tylko dużo informacji do zaprezentowania na początku, w tym rzeczy, które utrudniają tego typu zmianę. Pobrane z powyższych podsumowań, musimy:
- upuść i ponownie utwórz odpowiednie indeksy w głównej tabeli
- zmień lub usuń kolumny obliczone, które obejmują kolumnę IDENTITY
- upuść indeksy w zindeksowanych widokach, które odwołują się do kolumny IDENTITY
- radzić sobie z przychodzącymi kluczami obcymi, które wskazują na kolumnę IDENTITY
Niestety wiele z tych rzeczy to paragraf 22. Nie możesz zmienić kolumny, ponieważ indeks na niej opiera się, i nie możesz zmienić indeksu, dopóki kolumna się nie zmieni. Czy nie byłoby wspaniale, gdyby ALTER INDEX obsługiwał REBUILD WITH (ONLINE = ON, CHANGE_COLUMN (COLUMN = ID, NEW_TYPE = BIGINT))
? Oraz CASCADE_CHANGE_TO_REFERENCING_KEYS,COLUMNS,INDEXES,VIEWS,ETC
? Cóż, nie (sprawdziłem). Dlatego musimy znaleźć sposoby na ułatwienie tych rzeczy. Czekajcie na część 3.
—
[ Część 1 | Część 2 | Część 3 | Część 4 ]