Usuń tożsamość z kolumny w tabeli

127

Mamy tabelę o pojemności 5 GB (prawie 500 milionów wierszy) i chcemy usunąć właściwość identity z jednej z kolumn, ale kiedy próbujemy to zrobić przez SSMS - to mija.

Czy można to zrobić za pomocą T-SQL?

Conrad Jagger
źródło
1
Czy możesz zamieścić tutaj schemat tabeli?
Al W,
Jestem pewien, że istnieją doskonałe powody, dla których SQL Server nie obsługuje usuwania właściwości tożsamości z kolumny za pomocą prostej instrukcji ALTER TABLE ..., ale mimo to jest mi smutno, że tak właśnie jest.
Jon Schneider,

Odpowiedzi:

143

Nie można usunąć IDENTITYraz ustawionej specyfikacji.

Aby usunąć całą kolumnę:

ALTER TABLE yourTable
DROP COLUMN yourCOlumn;

Informacje o ALTER TABLE tutaj

Jeśli chcesz zachować dane, ale usunąć IDENTITYkolumnę, będziesz musiał:

  • Utwórz nową kolumnę
  • Przenieś dane z istniejącej IDENTITYkolumny do nowej kolumny
  • Usuń istniejącą IDENTITYkolumnę.
  • Zmień nazwę nowej kolumny na oryginalną nazwę kolumny
Adam Wenger
źródło
3
możesz usunąć specyfikację tożsamości. W rzeczywistości musiałem to zrobić wczoraj przy użyciu SSMS, chociaż nie na 500 milionach wierszy.
Simon
34
@simon, jeśli skryptujesz swoje zmiany, zobaczysz, że SSMS faktycznie tworzy kopię tabeli bez właściwości tożsamości.
Code Magician
3
Chcę tylko dodać, aby zmienić nazwę nowej kolumny na nazwę oryginalnej kolumny. Ponadto, jeśli ta identitykolumna jest używana jako część a foreign keyw innej tabeli, będziesz musiał najpierw usunąć ograniczenia, a następnie podjąć działania, o których @AdamWenger wspomniał o usuwaniu tożsamości. attribute/propertyMożesz również zajrzeć do tego linku, aby uzyskać więcej informacji o usunięciu samego atrybutu: blog.sqlauthority.com/2009/05/03/… ..Powodzenia!
Nonym
1
Osobie, która zagłosowała w dół - byłbym wdzięczny za usłyszenie, co ci się nie podoba w mojej odpowiedzi.
Adam Wenger,
1
Spójrz na odpowiedź Marka Sowula poniżej. Eliminuje potrzebę przenoszenia danych z kolumny do kolumny. Używając odpowiedzi Marka, tylko tasujesz metadane. Nic wielkiego, chyba że pracujesz nad tabelą, która ma dziesiątki lub setki milionów wierszy. Odpowiedź znaku plusa zapobiega przeniesieniu kolumny w schemacie tabeli. Po prostu spróbowałem i zadziałało jak urok. Bardzo mądry.
Andrew Steitz
101

Jeśli chcesz to zrobić bez dodawania i wypełniania nowej kolumny , bez zmiany kolejności kolumn i prawie bez przestojów, ponieważ żadne dane nie zmieniają się w tabeli, zróbmy trochę magii z funkcją partycjonowania (ale ponieważ żadne partycje nie są używane, nie t potrzebujesz wersji Enterprise):

  1. Usuń wszystkie klucze obce wskazujące na tę tabelę
  2. Skrypt tabeli, która ma zostać utworzona; zmień nazwę wszystkiego, np. „MyTable2”, „MyIndex2”, itp. Usuń specyfikację IDENTITY.
  3. Powinieneś teraz mieć dwie „identyczne” tabele, jedną pełną, drugą pustą bez TOŻSAMOŚCI.
  4. Biegać ALTER TABLE [Original] SWITCH TO [Original2]
  5. Twoja oryginalna tabela będzie teraz pusta, a nowa będzie zawierała dane. Zmieniłeś metadane dla dwóch tabel (natychmiast).
  6. Usuń oryginalną (teraz pustą tabelę), exec sys.sp_renameaby zmienić nazwy różnych obiektów schematu z powrotem na oryginalne nazwy, a następnie możesz odtworzyć klucze obce.

Na przykład, biorąc pod uwagę:

CREATE TABLE Original
(
  Id INT IDENTITY PRIMARY KEY
, Value NVARCHAR(300)
);
CREATE NONCLUSTERED INDEX IX_Original_Value ON Original (Value);

INSERT INTO Original
SELECT 'abcd'
UNION ALL 
SELECT 'defg';

Możesz wykonać następujące czynności:

--create new table with no IDENTITY
CREATE TABLE Original2
(
  Id INT PRIMARY KEY
, Value NVARCHAR(300)
);
CREATE NONCLUSTERED INDEX IX_Original_Value2 ON Original2 (Value);

--data before switch
SELECT 'Original', *
FROM Original
UNION ALL
SELECT 'Original2', *
FROM Original2;

ALTER TABLE Original SWITCH TO Original2;

--data after switch
SELECT 'Original', *
FROM Original
UNION ALL
SELECT 'Original2', *
FROM Original2;

--clean up 
IF NOT EXISTS (SELECT * FROM Original) DROP TABLE Original;
EXEC sys.sp_rename 'Original2.IX_Original_Value2', 'IX_Original_Value', 'INDEX';
EXEC sys.sp_rename 'Original2', 'Original', 'OBJECT';


UPDATE Original
SET Id = Id + 1;

SELECT *
FROM Original;
Mark Sowul
źródło
Nie mam czasu, żeby tego spróbować, ale dobrze jest wiedzieć, kiedy następnym razem naprawdę będę musiał porzucić tożsamość.
CoderDennis
12
To powinna być akceptowana odpowiedź. To jedyny prawdziwy sposób na usunięcie kolumny tożsamości bez masowej migracji danych.
Vaccano
Łał! To bardzo sprytne.
Andrew Steitz
Ta odpowiedź jest fenomenalna! Dzięki!
Jon
5
Jeśli używasz SQL Management Studio do skryptu tabeli, pamiętaj, aby włączyć Narzędzia> Opcje> Eksplorator obiektów SQL Server> Skrypty> Opcje tabeli i widoku> Indeksy skryptów (domyślnie False)
user423430
61

To staje się kłopotliwe z ograniczeniami klucza obcego i podstawowego, więc oto kilka skryptów, które pomogą Ci w drodze:

Najpierw utwórz zduplikowaną kolumnę z tymczasową nazwą:

alter table yourTable add tempId int NOT NULL default -1;
update yourTable set tempId = id;

Następnie uzyskaj nazwę ograniczenia klucza podstawowego:

SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = 'yourTable';

Teraz spróbuj usunąć ograniczenie klucza podstawowego dla swojej kolumny:

ALTER TABLE yourTable DROP CONSTRAINT PK_yourTable_id;

Jeśli masz klucze obce, to się nie powiedzie, więc jeśli tak, porzuć ograniczenia klucza obcego. ŚLEDŹ, KTÓRE TABELE URUCHOMISZ, ABY PÓŹNIEJ DODAĆ OGRANICZENIA !!!

SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = 'otherTable';
alter table otherTable drop constraint fk_otherTable_yourTable;
commit;
..

Po usunięciu wszystkich ograniczeń klucza obcego będziesz mógł usunąć ograniczenie PK, upuścić tę kolumnę, zmienić nazwę kolumny tymczasowej i dodać ograniczenie PK do tej kolumny:

ALTER TABLE yourTable DROP CONSTRAINT PK_yourTable_id;
alter table yourTable drop column id;
EXEC sp_rename 'yourTable.tempId', 'id', 'COLUMN';
ALTER TABLE yourTable ADD CONSTRAINT PK_yourTable_id PRIMARY KEY (id) 
commit;

Na koniec dodaj ograniczenia FK z powrotem w:

alter table otherTable add constraint fk_otherTable_yourTable foreign key (yourTable_id) references yourTable(id);
..

El Fin!

Briguy37
źródło
Zwróć uwagę na swoją wersję serwera sql, próbowałem na serwerze Azure sql, nie wszystkie operacje tutaj są obsługiwane w wersji Azure sql.
dni
Dziękuję za odpowiedź! Bardzo mi pomogło! Po prostu należy dodać weryfikację dla indeksów, które odwołują się do kolumny. Coś w rodzaju: Select t.name 'Table', i.name 'Index', c.name 'Column', i.is_primary_key, i.is_unique From sys.tables t Inner Join sys.indexes i On i.object_id = t.object_id Inner Join sys.index_columns ic ON ic.object_id = i.object_id And i.index_id = ic.index_id Inner Join sys.columns c ON ic.object_id = c.object_id and ic.column_id = c.column_id Where t.name = 'tableName' Order By t.name, i.name, i.index_id, ic.index_column_id
Alexandre Junges
dobra sztuczka, bardzo mi pomogła, ale jedną kwestią, o której chcę tutaj wspomnieć, jest to, że kiedy dodajemy nową zduplikowaną kolumnę, jak wspomniano w pierwszym kroku, dodano ją na końcu, ale ogólnie chcemy, aby nasza kolumna klucza podstawowego, tj. pierwsza kolumna w tej tabeli. Czy jest jakieś obejście tego problemu?
Ashish Shukla
19

Po prostu miałem ten sam problem. 4 instrukcje w SSMS zamiast używania GUI i było to bardzo szybkie.

  • Utwórz nową kolumnę

    alter table users add newusernum int;

  • Skopiuj wartości

    update users set newusernum=usernum;

  • Upuść starą kolumnę

    alter table users drop column usernum;

  • Zmień nazwę nowej kolumny na starą nazwę kolumny

    EXEC sp_RENAME 'users.newusernum' , 'usernum', 'COLUMN';

wolność_
źródło
Nie jest to takie proste, jeśli masz jakieś ograniczenia na starej kolumnie.
Suncat2000
13

Poniższy skrypt usuwa pole Identity dla kolumny o nazwie „Id”

Mam nadzieję, że to pomoże.

BEGIN TRAN
BEGIN TRY
    EXEC sp_rename '[SomeTable].[Id]', 'OldId';

    ALTER TABLE [SomeTable] ADD Id int NULL

    EXEC ('UPDATE [SomeTable] SET Id = OldId')

    ALTER TABLE [SomeTable] NOCHECK CONSTRAINT ALL

    ALTER TABLE [SomeTable] DROP CONSTRAINT [PK_constraintName];
    ALTER TABLE [SomeTable] DROP COLUMN OldId
    ALTER TABLE [SomeTable] ALTER COLUMN [Id] INTEGER NOT NULL
    ALTER TABLE [SomeTable] ADD CONSTRAINT PK_JobInfo PRIMARY KEY (Id)

    ALTER TABLE [SomeTable] CHECK CONSTRAINT ALL

    COMMIT TRAN
END TRY
BEGIN CATCH
    ROLLBACK TRAN   
    SELECT ERROR_MESSAGE ()
END CATCH
bluedot
źródło
4

Poniżej kod działa dobrze, gdy nie znamy nazwy kolumny tożsamości .

Musisz skopiować dane do nowej tabeli tymczasowej, takiej jak Invoice_DELETED. i następnym razem użyjemy:

insert into Invoice_DELETED select * from Invoice where ...


SELECT t1.*
INTO Invoice_DELETED
FROM Invoice t1
LEFT JOIN Invoice ON 1 = 0
--WHERE t1.InvoiceID = @InvoiceID

Więcej informacji można znaleźć pod adresem : https://dba.stackexchange.com/a/138345/101038

Zolfaghari
źródło
1
MIŁOŚĆ YAAAAAAAAAA. Dokładnie to, czego szukałem, łatwy sposób na uniknięcie generowania „TOŻSAMOŚCI” z „SELECT * INTO” (aby utworzyć nowy temp tabeli dla potrzebnych kopii zapasowych)
KurzedMetal
jest mały i piękny;)
Zolfaghari
3
ALTER TABLE tablename add newcolumn int
update tablename set newcolumn=existingcolumnname
ALTER TABLE tablename DROP COLUMN existingcolumnname;
EXEC sp_RENAME 'tablename.oldcolumn' , 'newcolumnname', 'COLUMN'

Jednak powyższy kod działa tylko wtedy, gdy nie ma relacji klucza podstawowego-obcego

Jekin Kalariya
źródło
1

Tylko dla kogoś, kto ma ten sam problem, co ja. Jeśli chcesz tylko raz zrobić wstawkę, możesz zrobić coś takiego.

Załóżmy, że masz tabelę z dwiema kolumnami

ID Identity (1,1) | Name Varchar

i chcesz wstawić wiersz o numerze ID = 4. Więc zmieniasz go na 3, więc następny to 4

DBCC CHECKIDENT([YourTable], RESEED, 3)

Zrób wstawkę

INSERT  INTO [YourTable]
        ( Name )
VALUES  ( 'Client' )

I wróć do najwyższego ID, przypuśćmy, że jest to 15

DBCC CHECKIDENT([YourTable], RESEED, 15)

Gotowe!

Faruk Feres
źródło
1

Miałem te same wymagania i możesz spróbować w ten sposób, co osobiście ci polecam, proszę ręcznie zaprojektować swoją tabelę i wygenerować skrypt, a to, co poniżej zrobiłem, to zmiana nazwy starej tabeli, a także jej ograniczenie do tworzenia kopii zapasowych.

/* To prevent any potential data loss issues, you should review this script in detail before running it outside the context of the database designer.*/
BEGIN TRANSACTION

SET QUOTED_IDENTIFIER ON
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
COMMIT
BEGIN TRANSACTION
GO
ALTER TABLE dbo.SI_Provider_Profile
    DROP CONSTRAINT DF_SI_Provider_Profile_SIdtDateTimeStamp
GO
ALTER TABLE dbo.SI_Provider_Profile
    DROP CONSTRAINT DF_SI_Provider_Profile_SIbHotelPreLoaded
GO
CREATE TABLE dbo.Tmp_SI_Provider_Profile
    (
    SI_lProvider_Profile_ID int NOT NULL,
    SI_lSerko_Integrator_Token_ID int NOT NULL,
    SI_sSerko_Integrator_Provider varchar(50) NOT NULL,
    SI_sSerko_Integrator_Profile varchar(50) NOT NULL,
    SI_dtDate_Time_Stamp datetime NOT NULL,
    SI_lProvider_ID int NULL,
    SI_sDisplay_Name varchar(10) NULL,
    SI_lPurchased_From int NULL,
    SI_sProvider_UniqueID varchar(255) NULL,
    SI_bHotel_Pre_Loaded bit NOT NULL,
    SI_sSiteName varchar(255) NULL
    )  ON [PRIMARY]
GO
ALTER TABLE dbo.Tmp_SI_Provider_Profile SET (LOCK_ESCALATION = TABLE)
GO
ALTER TABLE dbo.Tmp_SI_Provider_Profile ADD CONSTRAINT
    DF_SI_Provider_Profile_SIdtDateTimeStamp DEFAULT (getdate()) FOR SI_dtDate_Time_Stamp
GO
ALTER TABLE dbo.Tmp_SI_Provider_Profile ADD CONSTRAINT
    DF_SI_Provider_Profile_SIbHotelPreLoaded DEFAULT ((0)) FOR SI_bHotel_Pre_Loaded
GO
IF EXISTS(SELECT * FROM dbo.SI_Provider_Profile)
        EXEC('INSERT INTO dbo.Tmp_SI_Provider_Profile (SI_lProvider_Profile_ID, SI_lSerko_Integrator_Token_ID, SI_sSerko_Integrator_Provider, SI_sSerko_Integrator_Profile, SI_dtDate_Time_Stamp, SI_lProvider_ID, SI_sDisplay_Name, SI_lPurchased_From, SI_sProvider_UniqueID, SI_bHotel_Pre_Loaded, SI_sSiteName)
        SELECT SI_lProvider_Profile_ID, SI_lSerko_Integrator_Token_ID, SI_sSerko_Integrator_Provider, SI_sSerko_Integrator_Profile, SI_dtDate_Time_Stamp, SI_lProvider_ID, SI_sDisplay_Name, SI_lPurchased_From, SI_sProvider_UniqueID, SI_bHotel_Pre_Loaded, SI_sSiteName FROM dbo.SI_Provider_Profile WITH (HOLDLOCK TABLOCKX)')
GO

-- Rename the primary key constraint or unique key In SQL Server constraints such as primary keys or foreign keys are objects in their own right, even though they are dependent upon the "containing" table.
EXEC sp_rename 'dbo.SI_Provider_Profile.PK_SI_Provider_Profile', 'PK_SI_Provider_Profile_Old';
GO
-- backup old table in case of 
EXECUTE sp_rename N'dbo.SI_Provider_Profile', N'SI_Provider_Profile_Old', 'OBJECT'
GO

EXECUTE sp_rename N'dbo.Tmp_SI_Provider_Profile', N'SI_Provider_Profile', 'OBJECT'
GO

ALTER TABLE dbo.SI_Provider_Profile ADD CONSTRAINT
    PK_SI_Provider_Profile PRIMARY KEY NONCLUSTERED 
    (
    SI_lProvider_Profile_ID
    ) WITH( PAD_INDEX = OFF, FILLFACTOR = 90, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

GO
COMMIT TRANSACTION
Cena Jang
źródło
1

W SQL Server możesz włączać i wyłączać wstawianie tożsamości w następujący sposób:

USTAW IDENTITY_INSERT nazwa_tabeli ON

- uruchom tutaj swoje zapytania

WYŁĄCZ IDENTITY_INSERT nazwa_tabeli

user2624918
źródło