Database
 sql >> Baza danych >  >> RDS >> Database

Minimalizowanie wpływu poszerzenia kolumny TOŻSAMOŚĆ – część 3

[ Część 1 | Część 2 | Część 3 | Część 4 ]

Jak dotąd w tej serii zademonstrowałem bezpośredni fizyczny wpływ na stronę podczas rozbudowy z int do bigint , a następnie przeszła przez kilka typowych programów blokujących do tej operacji. W tym poście chciałem zbadać dwa potencjalne obejścia:jedno proste, a drugie niezwykle zawiłe.

Łatwy sposób

Zostałem trochę okradziony z mojego grzmotu w komentarzu do mojego poprzedniego postu – Keith Monroe zasugerował, że możesz po prostu przestawić tabelę na niższe negatywy ograniczenie typu danych całkowitych, podwajając pojemność dla nowych wartości. Możesz to zrobić za pomocą DBCC CHECKIDENT :

DBCC CHECKIDENT (N'dbo.TableName', RESEED, -2147483648);

Może to zadziałać, zakładając, że wartości zastępcze nie mają znaczenia dla użytkowników końcowych (lub, jeśli tak, to użytkownicy nie będą przestraszeni nagłymi liczbami ujemnymi). Przypuszczam, że można ich oszukać widokiem:

CREATE VIEW dbo.ViewNameAS SELECT ID =CONVERT(bigint, CASE WHEN ID <0 THEN (2147483648*2) - 1 + CONVERT(bigint, ID) ELSE ID END) FROM dbo.TableName;

Oznacza to, że użytkownik, który dodał ID = -2147483648 faktycznie zobaczy +2147483648 , użytkownik, który dodał ID = -2147483647 zobaczy +2147483649 , i tak dalej. Musiałbyś jednak dostosować inny kod, aby upewnić się, że wykonasz odwrotne obliczenia, gdy użytkownik przejdzie w tym ID , np.

ZMIANA PROCEDURY dbo.GetRowByID @ID bigintASBEGIN USTAWIĆ NOCOUNT ON; ZADEKLARUJ @RealID bigint; SET @RealID =CASE WHEN @ID> 2147483647 THEN @ID - (2147483648*2) + 1 ELSE @ID END; SELECT ID, @ID /*, inne kolumny */ FROM dbo.TableName WHERE ID =@RealID;ENDGO

Nie przepadam za tym zaciemnianiem. W ogóle. Jest niechlujny, mylący i podatny na błędy. Zachęca też do wglądu w klucze zastępcze – ogólnie IDENTITY wartości nie powinny być ujawniane użytkownikom końcowym, więc naprawdę nie powinno ich obchodzić, czy są to klienci 24, 642, -376, czy znacznie większe liczby po obu stronach zera.

To „rozwiązanie” zakłada również, że nie masz nigdzie kodu, który zamawia według IDENTITY kolumna w celu zaprezentowania ostatnio wstawionych wierszy jako pierwsza, lub wywnioskuje, że najwyższa IDENTITY wartość musi być najnowszym wierszem. Kod, który robi polegać na porządku sortowania IDENTITY kolumna, jawnie lub niejawnie (co może być więcej niż myślisz, jeśli jest to indeks klastrowy), nie będzie już przedstawiać wierszy w oczekiwanej kolejności – pokaże wszystkie wiersze utworzone po RESEED , zaczynając od pierwszego, a następnie pokaże wszystkie wiersze utworzone przed RESEED , zaczynając od pierwszego.

Podstawową zaletą tego podejścia jest to, że nie wymaga zmiany typu danych, w wyniku czego RESEED zmiana nie wymaga żadnych zmian w indeksach, ograniczeniach ani przychodzących kluczach obcych.

Minusem – oprócz wspomnianych powyżej zmian w kodzie, oczywiście – jest to, że kupujesz czas tylko w krótkim okresie. W końcu wyczerpiesz również wszystkie dostępne ujemne liczby całkowite. I nie myśl, że to podwaja żywotność obecnej wersji tabeli pod względem czasu – w wielu przypadkach przyrost danych przyspiesza, a nie jest stały, więc następne 2 miliardy wierszy zużyjesz znacznie szybciej niż pierwsze 2 miliardy.

Trudniejsza droga

Innym podejściem, które możesz zastosować, jest zaprzestanie używania IDENTITY kolumna w całości; zamiast tego możesz przekonwertować na użycie SEQUENCE . Możesz utworzyć nowy bigint kolumna, ustaw domyślną następną wartość z SEQUENCE , zaktualizuj wszystkie te wartości wartościami z oryginalnej kolumny (w razie potrzeby w partiach), usuń oryginalną kolumnę i zmień nazwę nowej kolumny. Stwórzmy tę fikcyjną tabelę i wstawmy jeden wiersz:

CREATE TABLE dbo.SequenceDemo(ID int IDENTITY(1,1), x char(1),CONSTRAINT PK_SD_Identity PRIMARY KEY CLUSTERED (ID));GO INSERT dbo.SequenceDemo(x) VALUES('x');

Następnie utworzymy SEQUENCE która zaczyna się tuż za górną granicą int:

UTWÓRZ SEKWENCJĘ dbo.BeyondIntAS bigintSTART Z PRZYROSTEM 2147483648 O 1;

Następnie zmiany w tabeli niezbędne do przełączenia się na użycie SEQUENCE dla nowej kolumny:

ROZPOCZNIJ TRANSAKCJĘ; — dodaj nową kolumnę „tożsamość”:ALTER TABLE dbo.SequenceDemo ADD ID2 bigint;GO — ustaw nową kolumnę na równą istniejącym wartościom tożsamości — w przypadku dużych tabel może być konieczne wykonanie tego w partiach:UPDATE dbo.SequenceDemo ZESTAW ID2 =ID; -- teraz zmień wartość null i dodaj wartość domyślną z naszej SEQUENCE:ALTER TABLE dbo.SequenceDemo ALTER COLUMN ID2 bigint NOT NULL;ALTER TABLE dbo.SequenceDemo DODAJ OGRANICZENIE DF_SD_Identity DOMYŚLNA NASTĘPNA WARTOŚĆ DLA dbo.BeyondInt DLA ID2; -- trzeba usunąć istniejący plik PK (i wszelkie indeksy):ALTER TABLE dbo.SequenceDemo DROP CONSTRAINT PK_SD_Identity; -- usuń starą kolumnę i zmień nazwę nowej:ALTER TABLE dbo.SequenceDemo DROP COLUMN ID;EXEC sys.sp_rename N'dbo.SequenceDemo.ID2', N'ID', 'COLUMN'; -- teraz umieść kopię zapasową PK:ALTER TABLE dbo.SequenceDemo ADD CONSTRAINT PK_SD_Identity PRIMARY KEY CLUSTERED (ID); POTWIERDZENIE TRANSAKCJI;

W takim przypadku następna wstawka dałaby następujące wyniki (zwróć uwagę, że SCOPE_IDENTITY() nie zwraca już prawidłowej wartości):

INSERT dbo.SequenceDemo(x) VALUES('y');SELECT Si =SCOPE_IDENTITY();SELECT ID, x FROM dbo.SequenceDemo; /* wyniki Si----NULL ID x---------- -1 x2147483648 y */

Jeśli tabela jest duża i musisz zaktualizować nową kolumnę w partiach zamiast powyższej jednorazowej transakcji, jak opisałem tutaj – umożliwiając użytkownikom interakcję z tabelą w międzyczasie – będziesz potrzebować wyzwalacza w miejscu, aby zastąpić SEQUENCE wartość dla wszystkich nowych wierszy, które są wstawiane, tak aby nadal odpowiadały temu, co jest wyprowadzane do dowolnego kodu wywołującego. (Zakłada to również, że nadal masz miejsce w zakresie liczb całkowitych, aby nadal akceptować niektóre aktualizacje; w przeciwnym razie, jeśli już wyczerpałeś zakres, będziesz musiał zrobić sobie przerwę – lub skorzystaj z prostego rozwiązania powyżej w krótkim okresie .)

Porzućmy wszystko i zacznijmy od nowa, a potem po prostu dodajmy nową kolumnę:

DROP TABLE dbo.SequenceDemo;DROP SEQUENCE dbo.BeyondInt;GO CREATE TABLE dbo.SequenceDemo(ID int IDENTITY(1,1), x char(1), CONSTRAINT PK_SD_Identity PRIMARY KEY CLUSTERED (ID));GO INSERT dbo .SequenceDemo(x) VALUES('x');GO UTWÓRZ SEKWENCJĘ dbo.BeyondIntAS bigintROZPOCZNIJ Z 2147483648 WZROST O 1;GO ALTER TABELA dbo.SequenceDemo DODAJ ID2 bigint;GO

A oto wyzwalacz, który dodamy:

CREATE TRIGGER dbo.After_SequenceDemoON dbo.SequenceDemoAFTER INSERTASBEGIN AKTUALIZACJA sd SET sd.ID2 =sd.ID Z dbo.SequenceDemo JAKO sd INNER JOIN wstawiono JAK i ON sd.ID =i.ID;END

Tym razem następna wstawka będzie nadal generować wiersze w dolnym zakresie liczb całkowitych dla obu kolumn, dopóki wszystkie wcześniej istniejące wartości nie zostaną zaktualizowane, a pozostałe zmiany zostaną zatwierdzone:

INSERT dbo.SequenceDemo(x) VALUES('y');SELECT Si =SCOPE_IDENTITY();SELECT ID, ID2, x FROM dbo.SequenceDemo; /* wyniki Si----2 ID ID2 x---- ---- -1 NULL x2 2 y */

Teraz możemy kontynuować aktualizację istniejącego ID2 wartości, podczas gdy nowe wiersze są nadal wstawiane w dolnym zakresie:

WŁĄCZ NR; ZADEKLARUJ @r INT =1; PODCZAS @r> 0POCZĄTEK ROZPOCZNIJ TRANSAKCJĘ; UPDATE TOP (10000) dbo.SequenceDemo USTAW ID2 =ID GDZIE ID2 JEST NULL; USTAW @r =@@LICZNIKWIER; POTWIERDZENIE TRANSAKCJI; -- PUNKT KONTROLNY; -- jeśli proste -- BACKUP LOG ... -- jeśli pełnyEND

Po zaktualizowaniu wszystkich istniejących wierszy możemy kontynuować wprowadzanie pozostałych zmian, a następnie odrzucić wyzwalacz:

ROZPOCZNIJ TRANSAKCJĘ;ALTER TABLE dbo.SequenceDemo ALTER COLUMN ID2 BIGINT NOT NULL;ALTER TABLE dbo.SequenceDemo DODAJ OGRANICZENIE DF_SD_Identity DOMYŚLNA NASTĘPNA WARTOŚĆ DLA dbo.BeyondInt DLA ID2;ALTER TABLE dbo_OPTablenceDemo DROP COLUMN ID;EXEC sys.sp_rename N'dbo.SequenceDemo.ID2', N'ID', 'COLUMN';ALTER TABLE dbo.SequenceDemo DODAJ OGRANICZENIE PK_SD_Identity PRIMARY KEY CLUSTERED (ID);DROP TRIGGER dbo.InnceACMOF_MSe pre> 

Teraz następna wstawka wygeneruje te wartości:

INSERT dbo.SequenceDemo(x) VALUES('z');SELECT Si =SCOPE_IDENTITY();SELECT ID, x FROM dbo.SequenceDemo; /* wyniki Si----NULL ID x---------- -1 x2 y2147483648 z */

Jeśli masz kod, który opiera się na SCOPE_IDENTITY() , @@IDENTITY lub IDENT_CURRENT() , musiałby się również zmienić, ponieważ te wartości nie są już wypełniane po wstawieniu — chociaż OUTPUT klauzula powinna nadal działać poprawnie w większości scenariuszy. Jeśli potrzebujesz kodu, aby nadal wierzyć, że tabela generuje IDENTITY wartość, możesz użyć wyzwalacza, aby to sfałszować – jednak będzie w stanie wypełnić tylko @@IDENTITY przy wstawianiu, a nie SCOPE_IDENTITY() . Może to nadal wymagać zmian, ponieważ w większości przypadków nie chcesz polegać na @@IDENTITY do czegokolwiek (więc jeśli zamierzasz wprowadzić zmiany, usuń wszystkie założenia dotyczące IDENTITY w ogóle).

CREATE TRIGGER dbo.FakeIdentityON dbo.SequenceDemoZAMIAST INSERTASBEGIN SET NOCOUNT ON; DECLARE @lowestID bigint =(wstawiono SELECT MIN (id) FROM); DECLARE @sql nvarchar(max) =N'DECLARE @foo TABLE(ID bigint IDENTITY(' + CONVERT(varchar(32), @lowestID) + N',1));'; SELECT @sql +=N'INSERT @foo WARTOŚCI DOMYŚLNE;' Z wstawiono; EXEC sys.sp_executesql @ sql; INSERT dbo.SequenceDemo(ID, x) SELECT ID, x FROM wstawiony;END

Teraz następna wstawka wygeneruje te wartości:

INSERT dbo.SequenceDemo(x) VALUES('a');SELECT Si =SCOPE_IDENTITY(), Ident =@@IDENTITY;SELECT ID, x FROM dbo.SequenceDemo; /* wyniki Si Ident---- -----NULL 2147483649 ID x---------- -1 x2 y2147483648 z2147483649 a */

Dzięki temu obejściu nadal będziesz musiał radzić sobie z innymi ograniczeniami, indeksami i tabelami z przychodzącymi kluczami obcymi. Lokalne ograniczenia i indeksy są dość proste, ale bardziej złożoną sytuacją z kluczami obcymi zajmę się w następnej części tej serii.

Taki, który nie zadziała, ale chciałbym, żeby tak było

ALTER TABLE SWITCH może być bardzo skutecznym sposobem na wprowadzenie pewnych zmian metadanych, które są trudne do wykonania w inny sposób. I wbrew powszechnemu przekonaniu, nie dotyczy to tylko partycjonowania i nie ogranicza się do wersji Enterprise. Poniższy kod będzie działał w Express i jest metodą, której ludzie używali do dodawania lub usuwania IDENTITY właściwość w tabeli (ponownie, bez uwzględniania kluczy obcych i wszystkich innych nieznośnych blokad).

CREATE TABLE dbo.WithIdentity( ID int IDENTITY(1,1) NOT NULL); CREATE TABLE dbo.WithoutIdentity( ID int NOT NULL); ALTER TABLE dbo.WithIdentity PRZEŁĄCZ NA dbo.WithoutIdentity;GO DROP TABLE dbo.WithIdentity;EXEC sys.sp_rename N'dbo.WithoutIdentity', N'dbo.WithIdentity', 'OBJECT';

Działa to, ponieważ typy danych i dopuszczalność wartości null są dokładnie takie same i nie zwraca się uwagi na IDENTITY atrybut. Spróbuj jednak mieszać typy danych, a rzeczy nie działają tak dobrze:

CREATE TABLE dbo.SourceTable(ID int IDENTITY(1,1) NOT NULL); CREATE TABLE dbo.TrySwitch(ID bigint IDENTITY(1,1) NOT NULL); ZMIEŃ TABELĘ dbo.SourceTable PRZEŁĄCZ NA dbo.TrySwitch;

Powoduje to:

Komunikat 4944, poziom 16, stan 1
Instrukcja ALTER TABLE SWITCH nie powiodła się, ponieważ kolumna „ID” ma typ danych int w tabeli źródłowej „dbo.SourceTable”, który różni się od typu bigint w tabeli docelowej „dbo.TrySwitch”.

Byłoby fantastycznie, gdyby SWITCH operacja może być użyta w scenariuszu takim jak ten, w którym jedyna różnica w schemacie w rzeczywistości nie *wymaga* żadnych fizycznych zmian w celu dostosowania (ponownie, jak pokazałem w części 1, dane są ponownie zapisywane na nowych stronach, mimo że nie ma takiej potrzeby).

Wniosek

W tym poście zbadaliśmy dwa potencjalne obejścia, które pozwolą Ci zyskać czas przed zmianą istniejącej IDENTITY kolumna lub porzucenie IDENTITY całkowicie teraz na korzyść SEQUENCE . Jeśli żadne z tych obejść nie jest dla Ciebie akceptowalne, obejrzyj część 4, w której zajmiemy się tym problemem bezpośrednio.

[ Część 1 | Część 2 | Część 3 | Część 4 ]


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Ogłoszenie ogólnej dostępności bezpiecznych kopii zapasowych SQL 8.7.2

  2. Jak pomnożyć dwie kolumny w SQL?

  3. Przeoczone perełki T-SQL

  4. Uzasadnienie nowego Maca Pro

  5. Wskazówki dotyczące przechowywania kopii zapasowych danych TimescaleDB w chmurze