Rimuovi identità da una colonna in una tabella


127

Abbiamo una tabella da 5 GB (quasi 500 milioni di righe) e vogliamo rimuovere la proprietà Identity su una delle colonne, ma quando proviamo a farlo tramite SSMS, scade.

È possibile farlo tramite T-SQL?


1
Puoi postare lo schema della tabella qui?
Al W

Sono sicuro che ci sono ottimi motivi per cui SQL Server non supporta la rimozione di una proprietà di identità da una colonna tramite una semplice istruzione ALTER TABLE ..., ma ciò nonostante mi rattrista al momento che sia così.
Jon Schneider

Risposte:


143

Non è possibile rimuovere una IDENTITYspecifica una volta impostata.

Per rimuovere l'intera colonna:

ALTER TABLE yourTable
DROP COLUMN yourCOlumn;

Informazioni su ALTER TABLE qui

Se è necessario mantenere i dati, ma rimuovere la IDENTITYcolonna, sarà necessario:

  • Crea una nuova colonna
  • Trasferisci i dati dalla IDENTITYcolonna esistente alla nuova colonna
  • Elimina l'esistente IDENTITY colonna .
  • Rinomina la nuova colonna con il nome della colonna originale

3
puoi rimuovere una specifica di identità. In effetti ho dovuto farlo ieri usando SSMS anche se non su 500 milioni di righe.
Simon

34
@simon se scrivi le tue modifiche, vedrai che SSMS sta effettivamente creando una copia della tabella senza la proprietà Identity.
Code Magician

3
Voglio solo aggiungere per rinominare la nuova colonna con il nome della colonna originale. Inoltre, se questa identitycolonna viene utilizzata come parte di un foreign keyin un'altra tabella, dovrai prima eliminare i vincoli, quindi agire come @AdamWenger ha menzionato sulla rimozione dell'identità attribute/property. Puoi anche esaminare questo collegamento per maggiori dettagli sulla rimozione solo dell'attributo: blog.sqlauthority.com/2009/05/03/… ..Buona fortuna!
Nonym

1
Alla persona che ha votato negativamente, apprezzerei sentire cosa non ti è piaciuto della mia risposta.
Adam Wenger

1
Dai un'occhiata alla risposta di Mark Sowul di seguito. Evita la necessità di spostare i dati da una colonna all'altra. Usando la risposta di Mark stai solo mescolando i metadati. Non è un grosso problema a meno che tu non stia lavorando su una tabella che ha decine o centinaia di milioni di righe. La risposta di Plus Mark impedisce il riposizionamento della colonna nello schema della tabella. L'ho appena provato e ha funzionato a meraviglia. Molto intelligente.
Andrew Steitz

100

Se vuoi farlo senza aggiungere e popolare una nuova colonna , senza riordinare le colonne e con quasi nessun tempo di inattività perché nessun dato sta cambiando sulla tabella, facciamo un po 'di magia con la funzionalità di partizionamento (ma poiché non vengono utilizzate partizioni non lo fai. t bisogno dell'edizione Enterprise):

  1. Rimuovere tutte le chiavi esterne che puntano a questa tabella
  2. Script la tabella da creare; rinominare tutto, ad esempio "MyTable2", "MyIndex2", ecc. Rimuovere la specifica IDENTITY.
  3. Ora dovresti avere due tabelle "identiche", una piena, l'altra vuota senza IDENTITÀ.
  4. Correre ALTER TABLE [Original] SWITCH TO [Original2]
  5. Ora la tua tabella originale sarà vuota e quella nuova avrà i dati. Hai cambiato i metadati per le due tabelle (istantaneo).
  6. Rilascia la tabella originale (ora vuota), exec sys.sp_renameper rinominare i vari oggetti dello schema con i nomi originali, quindi puoi ricreare le tue chiavi esterne.

Ad esempio, dato:

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';

Puoi fare quanto segue:

--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;

Non ho tempo per provarlo davvero, ma è una buona cosa saperlo la prossima volta che ho davvero bisogno di lasciare un'identità.
CoderDennis

12
Questa dovrebbe essere la risposta accettata. È l'unico vero modo per rimuovere una colonna di identità senza una massiccia migrazione dei dati.
Vaccano

Wow! Questo è molto intelligente.
Andrew Steitz

Questa risposta è fenomenale! Grazie!
Jon

5
Se si utilizza SQL Management Studio per eseguire lo scripting della tabella, assicurarsi di attivare Strumenti> Opzioni> Esplora oggetti di SQL Server> Scripting> Opzioni di tabella e visualizzazione> Indici di script (False per impostazione predefinita)
user423430

61

Questo diventa complicato con i vincoli della chiave esterna e primaria, quindi ecco alcuni script per aiutarti nel tuo cammino:

Innanzitutto, crea una colonna duplicata con un nome temporaneo:

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

Successivamente, ottieni il nome del tuo vincolo di chiave primaria:

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

Ora prova a eliminare il vincolo di chiave primaria per la tua colonna:

ALTER TABLE yourTable DROP CONSTRAINT PK_yourTable_id;

Se hai chiavi esterne, fallirà, quindi in tal caso elimina i vincoli di chiave esterna. TIENI TRACCIA DI QUALI TABELLE ESEGUI QUESTA COSÌ POTRAI AGGIUNGERE I VINCOLI IN SEGUITO !!!

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

Una volta rimossi tutti i vincoli di chiave esterna, sarai in grado di rimuovere il vincolo PK, eliminare quella colonna, rinominare la colonna temporanea e aggiungere il vincolo PK a quella colonna:

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;

Infine, aggiungi di nuovo i vincoli FK:

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

El Fin!


prestare attenzione alla versione del server sql, ho provato in azure sql server, non tutte le operazioni qui sono supportate nella versione azure sql.
dasons

Grazie per la risposta! Mi ha aiutato molto! Devi solo aggiungere una verifica per gli indici che fanno riferimento alla colonna. Qualcosa del tipo: 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

buon trucco, mi ha aiutato molto, Ma un punto, voglio menzionare qui è che, quando aggiungiamo una nuova colonna duplicata come menzionato nel primo passaggio, è stata aggiunta alla fine ma in genere vogliamo che la nostra colonna della chiave primaria, cioè Id, sia la prima colonna in quella tabella. Quindi c'è qualche soluzione alternativa a questo?
Ashish Shukla

19

Ho appena avuto lo stesso problema. 4 istruzioni in SSMS invece di utilizzare la GUI ed è stato molto veloce.

  • Crea una nuova colonna

    alter table users add newusernum int;

  • Copia i valori

    update users set newusernum=usernum;

  • Elimina la vecchia colonna

    alter table users drop column usernum;

  • Rinomina la nuova colonna con il vecchio nome della colonna

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


Non è così semplice se hai dei vincoli sulla vecchia colonna.
Suncat2000

13

Lo script seguente rimuove il campo Identity per una colonna denominata "Id"

Spero che sia d'aiuto.

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

4

Il codice seguente funziona bene, quando non conosciamo il nome della colonna di identità .

È necessario copiare i dati in una nuova tabella temporanea come Invoice_DELETED. e la prossima volta useremo:

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

Per ulteriori spiegazioni, vedere: https://dba.stackexchange.com/a/138345/101038


1
AMORE YAAAAAAAAAA. Esattamente quello che stavo cercando, un modo semplice per evitare di generare "IDENTITÀ" da un "SELEZIONA * INTO" (per creare una nuova temperatura della tabella per i backup necessari)
KurzedMetal

è piccolo e bello;)
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'

Tuttavia, il codice precedente funziona solo se non esiste una relazione di chiave primaria-esterna


1

Solo per qualcuno che ha lo stesso problema che ho avuto io. Se vuoi solo fare qualche inserto solo una volta puoi fare qualcosa del genere.

Supponiamo che tu abbia una tabella con due colonne

ID Identity (1,1) | Name Varchar

e vuoi inserire una riga con ID = 4. Quindi l'hai ridimensionata a 3 in modo che la successiva sia 4

DBCC CHECKIDENT([YourTable], RESEED, 3)

Crea l'inserto

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

E riporta il tuo seme all'ID più alto, supponiamo che sia 15

DBCC CHECKIDENT([YourTable], RESEED, 15)

Fatto!


1

Avevo lo stesso requisito e potresti provare in questo modo, che personalmente ti consiglio, per favore progetta manualmente la tua tabella e genera lo script, e quello che ho fatto di seguito è stato rinominare la vecchia tabella e anche il suo vincolo per il backup.

/* 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

1

In SQL Server puoi attivare e disattivare l'inserimento di identità in questo modo:

IMPOSTARE IDENTITY_INSERT nome_tabella su ON

- esegui le tue domande qui

IMPOSTARE IDENTITY_INSERT nome_tabella su OFF

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.