Remover identidade de uma coluna em uma tabela

127

Temos uma tabela de 5 GB (quase 500 milhões de linhas) e queremos remover a propriedade de identidade em uma das colunas, mas quando tentamos fazer isso através do SSMS, o tempo limite é excedido.

Isso pode ser feito através do T-SQL?

Conrad Jagger
fonte
1
Você pode postar o esquema da tabela aqui?
Al W
Tenho certeza de que existem excelentes razões pelas quais o SQL Server não oferece suporte à remoção de uma propriedade de identidade de uma coluna por meio de uma simples instrução ALTER TABLE ..., mas, no entanto, me deixa triste atualmente que esse seja o caso.
Jon Schneider

Respostas:

143

Você não pode remover uma IDENTITYespecificação depois de definida.

Para remover a coluna inteira:

ALTER TABLE yourTable
DROP COLUMN yourCOlumn;

Informações sobre ALTER TABLE aqui

Se você precisar manter os dados, mas remover a IDENTITYcoluna, precisará:

  • Crie uma nova coluna
  • Transfira os dados da IDENTITYcoluna existente para a nova coluna
  • Solte a IDENTITYcoluna existente .
  • Renomeie a nova coluna para o nome da coluna original
Adam Wenger
fonte
3
você pode remover uma especificação de identidade. Na verdade, eu tive que fazer isso ontem usando o SSMS, embora não em 500 milhões de linhas.
22611 Simon
33
@simon se você criar um script de suas alterações, verá que o SSMS está realmente criando uma cópia da tabela sem a propriedade de identidade.
Code Magician
3
Eu só quero adicionar para renomear a nova coluna para o nome da coluna original. Além disso, se essa identitycoluna for usada como parte de uma foreign keyem outra tabela, você terá que eliminar as restrições primeiro e depois agir como o @AdamWenger mencionou sobre a remoção da identidade. attribute/propertyVocê também pode consultar este link para obter mais detalhes sobre como remover apenas o atributo: blog.sqlauthority.com/2009/05/03/… .. Boa sorte!
Nonym
1
À pessoa que menos votou - eu gostaria de ouvir o que você não gostou na minha resposta.
Adam Wenger
1
Dê uma olhada na resposta de Mark Sowul abaixo. Evita a necessidade de mover dados de coluna para coluna. Usando a resposta de Mark, você está apenas embaralhando metadados. Não é grande coisa, a menos que você esteja trabalhando em uma tabela com dezenas ou centenas de milhões de linhas. Além disso, a resposta de Mark impede a realocação da coluna no esquema da tabela. Eu apenas tentei e funcionou como um encanto. Muito esperto.
Andrew Steitz
100

Se você quiser fazer isso sem adicionar e preencher uma nova coluna , sem reordenar as colunas e com quase nenhum tempo de inatividade porque nenhum dado está sendo alterado na tabela, vamos fazer alguma mágica com a funcionalidade de particionamento (mas como nenhuma partição é usada, você não não precisa da edição Enterprise):

  1. Remova todas as chaves estrangeiras que apontam para esta tabela
  2. Script da tabela a ser criada; renomeie tudo, como 'MyTable2', 'MyIndex2', etc. Remova a especificação IDENTITY.
  3. Agora você deve ter duas tabelas -ish "idênticas", uma cheia e a outra vazia sem IDENTIDADE.
  4. Corre ALTER TABLE [Original] SWITCH TO [Original2]
  5. Agora sua tabela original estará vazia e a nova tabela terá os dados. Você alterou os metadados para as duas tabelas (instantânea).
  6. Solte o original (tabela agora vazia), exec sys.sp_renamepara renomear os vários objetos de esquema de volta aos nomes originais e, em seguida, você pode recriar suas chaves estrangeiras.

Por exemplo, dado:

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

Você pode fazer o seguinte:

--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
fonte
Não tenho tempo para realmente tentar isso, mas é uma boa coisa saber da próxima vez que realmente preciso largar uma identidade.
CoderDennis
11
Essa deve ser a resposta aceita. É a única maneira real de remover uma coluna de identidade sem migração maciça de dados.
Vaccano 7/03/16
Uau! Isso é muito inteligente.
Andrew Steitz
Esta resposta é fenomenal! Obrigado!
Jon
5
Se estiver usando SQL Management Studio para o script para fora da mesa, não se esqueça de ligar Ferramentas> Opções> SQL Server Object Explorer> Scripting> tabela e exibição opções> índices de roteiro (false por padrão)
user423430
61

Isso fica confuso com restrições de chave estrangeira e primária, então, aqui estão alguns scripts para ajudá-lo no seu caminho:

Primeiro, crie uma coluna duplicada com um nome temporário:

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

Em seguida, obtenha o nome da sua restrição de chave primária:

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

Agora tente descartar a restrição de chave primária para sua coluna:

ALTER TABLE yourTable DROP CONSTRAINT PK_yourTable_id;

Se você tiver chaves estrangeiras, ela falhará; portanto, caso contrário, elimine as restrições de chave estrangeira. MANTENHA A TRILHA DE QUADROS QUE VOCÊ EXECUTA ASSIM PARA QUE PODE ADICIONAR AS RESTRIÇÕES MAIS TARDE !!!

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

Depois que todas as suas restrições de chave estrangeira forem removidas, você poderá remover a restrição PK, soltar essa coluna, renomear sua coluna temporária e adicionar a restrição PK a essa coluna:

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;

Por fim, adicione as restrições FK novamente em:

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

El Fin!

Briguy37
fonte
preste atenção à sua versão do servidor sql, tentei no azure sql server, nem todas as operações aqui são suportadas na versão azure sql.
31/03
Obrigado pela resposta! Me ajudou muito! Você só deve adicionar uma verificação para índices que fazem referência à coluna. Algo como: 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
bom truque, me ajudou muito, mas um ponto, quero mencionar aqui é que, quando adicionamos uma nova coluna duplicada, conforme mencionado no 1º passo, ela foi adicionada no final, mas geralmente queremos que a nossa coluna de chave primária seja a 1ª coluna nessa tabela. Então, existe alguma solução alternativa para isso?
Ashish Shukla
19

Eu apenas tive esse mesmo problema. 4 instruções no SSMS em vez de usar a GUI e foi muito rápido.

  • Faça uma nova coluna

    alter table users add newusernum int;

  • Copiar valores acima

    update users set newusernum=usernum;

  • Solte a coluna antiga

    alter table users drop column usernum;

  • Renomeie a nova coluna para o nome da coluna antiga

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

liberdade_
fonte
Não é tão simples se você tiver alguma restrição na coluna antiga.
Suncat2000
13

O script a seguir remove o campo Identidade de uma coluna chamada 'Id'

Espero que ajude.

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
fonte
4

Abaixo o código está funcionando bem quando não sabemos o nome da coluna de identidade .

Precisa copiar dados para uma nova tabela temporária como Invoice_DELETED. e da próxima vez que usarmos:

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

Para obter mais explicações, consulte: https://dba.stackexchange.com/a/138345/101038

Zolfaghari
fonte
1
AMOR YAAAAAAAAAA. Exatamente o que eu estava procurando, uma maneira fácil de geração de evitar a "identidade" de um "SELECT * INTO" (para criar uma nova temperatura mesa para backups essa necessidade)
KurzedMetal
ele é pequeno e beleza;)
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'

No entanto, o código acima funciona apenas se nenhuma relação de chave estrangeira primária

Jekin Kalariya
fonte
1

Apenas para alguém que tem o mesmo problema que eu. Se você quiser apenas inserir algumas vezes, pode fazer algo assim.

Vamos supor que você tenha uma tabela com duas colunas

ID Identity (1,1) | Name Varchar

e deseja inserir uma linha com o ID = 4. Então você aumentou para 3 para que o próximo seja 4

DBCC CHECKIDENT([YourTable], RESEED, 3)

Faça a inserção

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

E retorne sua semente ao ID mais alto, suponha que seja 15

DBCC CHECKIDENT([YourTable], RESEED, 15)

Feito!

Faruk Feres
fonte
1

Eu tinha o mesmo requisito, e você pode tentar dessa maneira, que eu pessoalmente recomendo, por favor, projete manualmente sua tabela e gere o script, e o que fiz abaixo foi renomear a tabela antiga e também sua restrição para 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
Cena Jang
fonte
1

No SQL Server, você pode ativar e desativar a inserção de identidade da seguinte maneira:

DEFINIR IDENTITY_INSERT table_name ON

- faça suas consultas aqui

DEFINIR IDENTITY_INSERT table_name OFF

user2624918
fonte