Dodaj autoinkrement do istniejącego PK

14

Utworzyłem tabelę w bazie danych, która już istnieje w innej bazie danych. Początkowo było wypełnione starymi danymi DB. PK tabeli musiało otrzymać wartości, które już istnieją w tych rekordach, więc nie mogło to być automatyczne zwiększenie.

Teraz potrzebuję nowego stołu, aby jego PK było automatycznie zwiększane. Ale jak mogę to zrobić, gdy PK już istnieje i ma dane?

Hikari
źródło
3
Kiedy mówisz „autoinkrement”, do czego dokładnie masz na myśli? W SQL Server nie ma takiej właściwości dla kolumny. Czy masz na myśli IDENTITY?
Max Vernon
Tak, tak to się nazywa w MSSQL. Ogólnie w bazie danych jest to autoinkrementacja PK.
Hikari,

Odpowiedzi:

14

Rozumiem twoje pytanie: masz istniejącą tabelę z kolumną, która do tej pory była wypełniona wartościami ręcznymi, a teraz chcesz (1) ustawić tę kolumnę jako IDENTITYkolumnę i (2) upewnić się, że IDENTITYpoczątki od najnowszej wartości w istniejących wierszach.

Po pierwsze, niektóre dane testowe do zabawy:

CREATE TABLE dbo.ident_test (
    id    int NOT NULL,
    xyz   varchar(10) NOT NULL,
    CONSTRAINT PK_ident_test PRIMARY KEY CLUSTERED (id)
);

INSERT INTO dbo.ident_test (id, xyz)
VALUES (1, 'test'),
       (2, 'test'),
       (5, 'test'),
       (6, 'test'),
       (10, 'test'),
       (18, 'test'),
       (19, 'test'),
       (20, 'test');

Celem jest, aby kolumny klucza podstawowego tabeli, w idAn IDENTITYkolumna, która rozpocznie się o godzinie 21 do następnego rekordu, który zostanie wstawiony. W tym przykładzie kolumna xyzreprezentuje wszystkie pozostałe kolumny tabeli.

Zanim cokolwiek zrobisz, przeczytaj ostrzeżenia na dole tego postu.

Po pierwsze, na wypadek, gdyby coś poszło nie tak:

BEGIN TRANSACTION;

Teraz dodajmy tymczasową kolumnę roboczą id_tempi ustawmy tę kolumnę na wartości istniejącej idkolumny:

ALTER TABLE dbo.ident_test ADD id_temp int NULL;
UPDATE dbo.ident_test SET id_temp=id;

Następnie musimy usunąć istniejącą idkolumnę (nie możesz po prostu „dodać” IDENTITYdo istniejącej kolumny, musisz utworzyć kolumnę jako IDENTITY). Klucz podstawowy również musi przejść, ponieważ kolumna zależy od niego.

ALTER TABLE dbo.ident_test DROP CONSTRAINT PK_ident_test;
ALTER TABLE dbo.ident_test DROP COLUMN id;

... i dodaj kolumnę ponownie, tym razem jako IDENTITY, wraz z kluczem podstawowym:

ALTER TABLE dbo.ident_test ADD id int IDENTITY(1, 1) NOT NULL;
ALTER TABLE dbo.ident_test ADD CONSTRAINT PK_ident_test PRIMARY KEY CLUSTERED (id);

Oto, gdzie robi się ciekawie. Możesz włączyć IDENTITY_INSERTw tabeli, co oznacza, że ​​możesz ręcznie zdefiniować wartości IDENTITYkolumny podczas wstawiania nowych wierszy (jednak nie aktualizując istniejących wierszy).

SET IDENTITY_INSERT dbo.ident_test ON;

Przy takim zestawie DELETEwszystkie wiersze w tabeli, ale wiersze, które usuwasz, znajdują się OUTPUTbezpośrednio w tej samej tabeli - ale z określonymi wartościami dla idkolumny (z kolumny zapasowej).

DELETE FROM dbo.ident_test
OUTPUT deleted.id_temp AS id, deleted.xyz
INTO dbo.ident_test (id, xyz);

Po zakończeniu IDENTITY_INSERTwyłącz ponownie.

SET IDENTITY_INSERT dbo.ident_test OFF;

Usuń tymczasową kolumnę, którą dodaliśmy:

ALTER TABLE dbo.ident_test DROP COLUMN id_temp;

I w końcu ponownie umieściłem IDENTITYkolumnę, więc następny rekord idzostanie wznowiony po najwyższej istniejącej liczbie w idkolumnie:

DECLARE @maxid int;
SELECT @maxid=MAX(id) FROM dbo.ident_test;
DBCC CHECKIDENT ("dbo.ident_test", RESEED, @maxid)

Sprawdzając przykładową tabelę, najwyższa idliczba to 20.

SELECT * FROM dbo.ident_test;

Dodaj kolejny wiersz i sprawdź jego nowy IDENTITY:

INSERT INTO dbo.ident_test (xyz) VALUES ('New row');
SELECT * FROM dbo.ident_test;

W tym przykładzie nowy wiersz będzie miał id=21. Wreszcie, jeśli jesteś zadowolony, dokonaj transakcji:

COMMIT TRANSACTION;

Ważny

Nie jest to trywialna operacja i niesie ze sobą całkiem sporo zagrożeń, o których powinieneś wiedzieć.

  • Zrób to w dedykowanym środowisku testowym. Miej kopie zapasowe. :)

  • Lubię go używać, BEGIN/COMMIT TRANSACTIONponieważ zapobiega bałaganowi innych procesów w tabeli, gdy jesteś w trakcie jej zmieniania, i daje ci możliwość przywrócenia wszystkiego do tyłu, jeśli coś pójdzie nie tak. Jednak każdy inny proces, który próbuje uzyskać dostęp do tabeli przed zatwierdzeniem transakcji, czeka. Może to być dość złe, jeśli masz duży stół i / lub pracujesz w środowisku produkcyjnym.

  • OUTPUT .. INTOnie będzie działać, jeśli tabela docelowa ma ograniczenia dla klucza obcego lub dowolną z wielu innych funkcji, których nie pamiętam z głowy. Zamiast tego możesz zamiast tego rozładować dane do tabeli tymczasowej, a następnie wstawić je z powrotem do oryginalnej tabeli. Możesz być w stanie używać przełączania partycji (nawet jeśli nie używasz partycji).

  • Uruchom te instrukcje jeden po drugim, a nie jako partia lub procedura składowana.

  • Spróbuj wymyślić inne rzeczy, które mogą zależeć od idupuszczanej i odtwarzanej kolumny. Wszelkie indeksy będą musiały zostać usunięte i ponownie utworzone (tak jak zrobiliśmy to z kluczem podstawowym). Pamiętaj, aby wykonać skrypt każdego indeksu i ograniczenia, które trzeba będzie wcześniej utworzyć.

  • Wyłącz dowolne INSERTi DELETEwyzwalacze na stole.

Jeśli ponowne utworzenie tabeli jest opcją:

Jeśli ponowne utworzenie tabeli jest dla Ciebie opcją, wszystko jest o wiele prostsze:

  • Tworzenie pustego stołu, z idkolumny jako IDENTITY,
  • Zestaw IDENTITY_INSERT ONdo stołu,
  • Wypełnij stół,
  • Ustaw IDENTITY_INSERT OFFi
  • Ponownie sprawdź tożsamość.
Daniel Hutmacher
źródło
Świetna odpowiedź, wielkie dzięki! Rzeczywiście w moim przypadku mogę go po prostu ustawić IDENTITY_INSERT ON, wypełnić i wyłączyć. Właśnie to chciałem zrobić, ale nie wiedziałem, że MSSQL to obsługuje.
Hikari,
5

Przenoszenie danych przy użyciu UPDATE, DELETE lub INSERT może zająć sporo czasu i zużyć zasoby (IO) zarówno na pliki danych, jak i pliki / dyski dziennika. Podczas pracy nad dużym stołem można uniknąć zapełniania dziennika transakcji potencjalnie dużą ilością rekordów: przy przełączaniu partycji zmieniane są tylko metadane.

Nie wiąże się to z przenoszeniem danych, dlatego odbywa się to bardzo szybko (niemal natychmiastowo).

Przykładowa tabela

Pytanie nie pokazuje oryginalnej tabeli DDL. Poniższy kod DDL zostanie użyty jako przykład w tej odpowiedzi:

CREATE TABLE dbo.idT(
    id int not null
    , uid uniqueidentifier not null
    , name varchar(50)
);
ALTER TABLE dbo.idT ADD CONSTRAINT PK_idT PRIMARY KEY CLUSTERED(id);

Przy pomocy tego zapytania dodaje się pół tuzina fałszywych identyfikatorów losowych od 0 do 15:

WITH ids(n) AS(
    SELECT x1.n+x2.n*4
    FROM (values(0), (3)) as x1(n)
    CROSS JOIN (values(0), (2), (3)) as x2(n)
)
INSERT INTO idt(id, uid, name)
SELECT n, NEWID(), NEWID() 
FROM ids

Przykładowe dane w IdT

id  uid                                     name
0   65533096-5007-43EA-88AD-D6776B3B94FA    6A69D4F2-D682-4168-A92F-4CD2E2DBC21D
3   CE87F1ED-BE1A-4F2D-8D62-E1ECA822D35B    AF0524D9-0DBB-41E1-883B-003CB4E4F012
8   34A1DBFD-4F92-4F34-9F04-4CDC824AB15A    02B4BDA4-D515-4262-9031-0BE496AC24CE
11  51606C95-9DE8-4C30-B23B-F915EEA41156    93258103-9C22-4F9C-85CF-712ED0FB3CE6
12  CEC80431-0513-4751-A250-0EB3390DACAB    2DA6B8AF-3EBC-42B3-A76C-028716E24661
15  5037EA83-286F-4EBC-AD7C-E237B570C1FF    095E51E9-8C38-4104-858F-D14AA810A550

Nowy stół z IDENTITY(0, 1)

Jedynym problemem idTjest brak IDENTITY(0, 1)właściwości na id. Nowa tabela o podobnej strukturze i IDENTITY(0, 1)utworzona:

CREATE TABLE dbo.idT_Switch(
    id int identity(0, 1) not null
    , uid uniqueidentifier not null
    , name varchar(50)
);
ALTER TABLE dbo.idT_Switch ADD CONSTRAINT PK_idT_Switch PRIMARY KEY CLUSTERED(id);

Oprócz IDENTITY(0, 1), idT_Switchjest identyczna idT.

Klucz obcy

Włączone klucze obce idTmuszą zostać usunięte, aby umożliwić użycie tej techniki.

Przełącznik partycji

idTI idT_Switchtabele mają zgodną strukturę. Zamiast używać DELETE, UPDATEoraz INSERTinstrukcje, aby przenieść wiersze od idTdo idT_Switchlub na idTsobie, ALTER TABLE ... SWITCHmożna stosować:

ALTER TABLE dbo.idT
SWITCH TO dbo.idT_Switch;

Pojedyncza „partycja” PK_idT(całej tabeli) zostaje przeniesiona do PK_idT_Switch(i odwrotnie). idTteraz zawiera 0 wierszy i idT_Switchzawiera 6 wierszy.

Pełną listę wymagań dotyczących zgodności źródeł i miejsc docelowych można znaleźć tutaj:

Wydajne przesyłanie danych za pomocą przełączania partycji

Zauważ, że to użycie SWITCHnie wymaga Enterprise Edition, ponieważ nie ma wyraźnego podziału na partycje. Niepodzielona na partycje tabela jest uważana za tabelę z pojedynczą partycją począwszy od SQL Server 2005.

Zastąpić idT

idT jest teraz pusty i bezużyteczny i można go upuścić:

DROP TABLE idT;

idT_Switchmożna zmienić nazwę i zastąpi starą idTtabelę:

EXECUTE sys.sp_rename
    @objname = N'dbo.idT_Switch',
    @newname = N'idT', -- note lack of schema prefix
    @objtype = 'OBJECT';

Klucz obcy

Klucze obce można ponownie dodać do nowej idTtabeli. Coś innego, co wcześniej usunięto, idTaby tabele były kompatybilne do przełączania, również będzie musiało zostać wykonane ponownie.

Reseed

SELECT IDENT_CURRENT( 'dbo.idT');

Ta komenda zwraca 0. Tabela idT zawiera 6 wierszy z MAX (id) = 15. Można użyć DBCC CHECKIDENT (nazwa_tabeli) :

DBCC CHECKIDENT ('dbo.idT');

Ponieważ 15 jest większe niż 0, zostanie ono automatycznie zresetowane bez szukania MAX (id):

Jeśli bieżąca wartość tożsamości dla tabeli jest mniejsza niż maksymalna wartość tożsamości przechowywana w kolumnie tożsamości, jest ona resetowana przy użyciu maksymalnej wartości w kolumnie tożsamości. Zobacz sekcję „Wyjątki” poniżej.

IDENT_CURRENT zwraca teraz 15 .

Przetestuj i dodaj dane

Proste INSERTstwierdzenie:

INSERT INTO idT(uid, name) SELECT NEWID(), NEWID();

Dodaje ten wiersz:

id  uid                                     name
16  B395D692-5D7B-4DFA-9971-A1497B8357A1    FF210D9E-4027-479C-B5D8-057E77FAF378

idKolumna jest teraz za pomocą tożsamości i nowa wartość jest faktycznie 16 (15 + 1).

Więcej informacji

Tutaj jest powiązane pytanie i odpowiedź z dodatkowym zapleczem SWITCHtechniki:

Dlaczego usunięcie właściwości Tożsamość w kolumnie nie jest obsługiwane

Julien Vavasseur
źródło
4

Jeśli chcesz zacząć od nowej wartości tożsamości, musisz ponownie wprowadzić swoją tożsamość. Zajrzyj do dokumentacji dlaCHECKIDENT

DBCC CHECKIDENT (yourtable, reseed, starting point)
Tom V - spróbuj topanswers.xyz
źródło
0

WŁĄCZ i WYŁĄCZ IDENTITY_INSERT

Jeśli twoja tabela to TABELA_A, to

  1. UTWÓRZ TABELĘ TABELA_B podobną do TABELI_A z kolumną tożsamości
  2. ZESTAW IDENTITY_INSERT TABLE_B WŁĄCZONY
  3. WSTAW do TABELI_B z TABELI_A
  4. ZESTAW IDENTITY_INSERT TABLE_B WYŁ
  5. DROP TABLE TABLE_A i zmień nazwę tabeli B Exec nazwa_programu „TABLE_B”, „TABLE_A”
użytkownik4321
źródło