Como alterar os valores da coluna de identidade programaticamente?

160

Eu tenho um banco de dados MS SQL 2005 com uma tabela Testcom coluna ID. IDé uma coluna de identidade.

Eu tenho linhas nesta tabela e todas elas têm seu valor incrementado automaticamente de ID correspondente.

Agora eu gostaria de alterar todos os IDs nesta tabela assim:

ID = ID + 1

Mas quando faço isso, recebo um erro:

Não é possível atualizar a coluna de identidade 'ID'.

Eu tentei isso:

    ALTER TABLE Test NOCHECK CONSTRAINT ALL 
    set identity_insert ID ON

Mas isso não resolve o problema.

Eu preciso ter a identidade definida para esta coluna, mas também preciso alterar os valores de tempos em tempos. Então, minha pergunta é como realizar essa tarefa.

Tomasz Smykowski
fonte

Respostas:

220

Você precisa

set identity_insert YourTable ON

Em seguida, exclua sua linha e reinsira-a com uma identidade diferente.

Depois de concluir a inserção, não se esqueça de desativar o identity_insert

set identity_insert YourTable OFF
AK
fonte
133
definir isso funcionará apenas ao inserir dados e não ao atualizar. A instrução UPDATE ainda falhará.
Husein Roncevic
31
Para uma atualização, você precisa excluir e reinserir. Não há outra maneira.
ashes999
1
@ MartinSmith, sua solução parece muito longa. Ser capaz de fazer isso em duas etapas (desativação de identidade, exclusão / inserção, ativação de identidade) é muito mais eficaz.
ashes999
3
@ ashes999 São 4 etapas e não 2. Minha resposta é apenas mais uma (criar tabela, alternar, atualizar, alternar, soltar) Presumivelmente, você está mais familiarizado com essas etapas, para que elas pareçam menos complicadas. A atualização pode ser muito mais eficiente e exclua a inserção quando houver muitas linhas envolvidas. Também onde você está segurando as linhas excluídas? Se uma #temptabela que você soltar é outra etapa, você não contou aqui.
Martin Smith
40

IDENTITY os valores da coluna são imutáveis.

No entanto, é possível alternar os metadados da tabela para remover a IDENTITYpropriedade, fazer a atualização e voltar.

Assumindo a seguinte estrutura

CREATE TABLE Test
(
ID INT IDENTITY(1,1) PRIMARY KEY,
X VARCHAR(10)
)

INSERT INTO Test 
OUTPUT INSERTED.*
SELECT 'Foo' UNION ALL
SELECT 'Bar' UNION ALL
SELECT 'Baz'

Então você pode fazer

/*Define table with same structure but no IDENTITY*/
CREATE TABLE Temp
(
ID INT PRIMARY KEY,
X VARCHAR(10)
)

/*Switch table metadata to new structure*/
ALTER TABLE Test SWITCH TO Temp;

/*Do the update*/
UPDATE Temp SET ID = ID + 1;

/*Switch table metadata back*/
ALTER TABLE Temp SWITCH TO Test;

/*ID values have been updated*/
SELECT *
FROM Test

/*Safety check in case error in preceding step*/
IF NOT EXISTS(SELECT * FROM Temp)
    DROP TABLE Temp /*Drop obsolete table*/

No SQL Server 2012, é possível ter uma coluna de incremento automático que também pode ser atualizada de maneira mais direta com SEQUENCES

CREATE SEQUENCE Seq
    AS INT
    START WITH 1
    INCREMENT BY 1

CREATE TABLE Test2
(
ID INT DEFAULT NEXT VALUE FOR Seq NOT NULL PRIMARY KEY,
X VARCHAR(10)
)

INSERT INTO Test2(X)
SELECT 'Foo' UNION ALL
SELECT 'Bar' UNION ALL
SELECT 'Baz'

UPDATE Test2 SET ID+=1
Martin Smith
fonte
1
Muito liso. Aprenda algo novo todos os dias (BTW eu testei isso no SQLExpress; apesar do SWITCH TO ele não usa particionamento, aparentemente). Note-se que a mesa tem que combinar bem-muito exatamente (índices, FKs, etc)
Mark Sowul
1
O método switch alterna quando você tem PK, FKs, Índice e Visualizações criados na tabela original? Eles vão quebrar usando esse método?
tj
1
@tj ao criar o script da tabela temporária na tabela de origem, incluindo todos os índices e restrições. Em seguida, altere os nomes da tabela e as restrições para evitar colisões. As visualizações não causam problemas, a menos que sejam indexadas ou vinculadas ao esquema.
Martin Smith
Não funciona se houver um índice de pesquisa de texto completo na tabela. Suponho que seja possível removê-lo e recriá-lo posteriormente, mas não testei.
youen
26

Por meio da interface do usuário no gerenciador do SQL Server 2005, altere a coluna e remova a propriedade de número automático (identidade) da coluna (selecione a tabela clicando com o botão direito do mouse nela e escolha "Design").

Em seguida, execute sua consulta:

UPDATE table SET Id = Id + 1

Em seguida, vá e adicione a propriedade autonumber novamente à coluna.

Michael Pryor
fonte
3
Se você fizer a alteração manualmente, poderá solicitar ao gerente que gere o script SQL para a alteração (menu do designer de tabela, gerar script de alteração). Para essa alteração, ele cria uma nova tabela e copia os dados e exclui o original.
Robin Bennett
2
@tomaszs - Um exemplo de código que também é mais eficiente, pois não reconstruir fisicamente a mesa em tudo (isso iria fazê-lo duas vezes) é na minha resposta final aqui
Martin Smith
Você não pode adicionar a identidade de volta. Você? stackoverflow.com/questions/1049210/…
Tomas Kubes
Obrigado. Isso foi perfeito. Eu tive uma linha na minha mesa que eu não estava ciente, então meus scripts do bootstrap para a minha mesa tinha IdAuto Identidade valores fora por 1.
Shiva
18

Em primeiro lugar, a configuração de IDENTITY_INSERT, ativada ou desativada, não funcionará para o que você precisa (é usada para inserir novos valores, como preencher lacunas).

Fazer a operação através da GUI apenas cria uma tabela temporária, copia todos os dados para uma nova tabela sem um campo de identidade e renomeia a tabela.

Miles D
fonte
9

Isso pode ser feito usando uma tabela temporária.

A ideia

  • desativar restrições (caso seu ID seja referenciado por uma chave estrangeira)
  • crie uma tabela temporária com o novo ID
  • excluir o conteúdo da tabela
  • copiar dados da tabela copiada para a tabela original
  • habilitar restrições desativadas anteriormente

Consultas SQL

Digamos que sua testtabela tenha duas colunas adicionais ( column2e column3) e que existem 2 tabelas com referências de chaves estrangeiras testchamadas foreign_table1e foreign_table2(porque os problemas da vida real nunca são simples).

alter table test nocheck constraint all;
alter table foreign_table1 nocheck constraint all;
alter table foreign_table2 nocheck constraint all;
set identity_insert test on;

select id + 1 as id, column2, column3 into test_copy from test v;
delete from test;
insert into test(id, column2, column3)
select id, column2, column3 from test_copy

alter table test check constraint all;
alter table foreign_table1 check constraint all;
alter table foreign_table2 check constraint all;
set identity_insert test off;
drop table test_copy;

É isso aí.

Manitra Andriamitondra
fonte
6

DBCC CHECKIDENT ('databasename.dbo.orders', RESEED, 999), você pode alterar qualquer número de coluna de identidade com esse comando e também pode iniciar esse número de campo de qualquer número que desejar. Por exemplo, no meu comando, peço para começar de 1000 (999 + 1) espero que seja o suficiente ... boa sorte

Roxana
fonte
4

Se a coluna não for um PK, você sempre poderá criar uma coluna NOVA na tabela com os números incrementados, solte o original e altere o novo para o antigo.

curioso para saber por que você pode precisar fazer isso ... o máximo que eu já tive que mexer nas colunas Identity foi preencher números e acabei usando o DBCC CHECKIDENT (nome da tabela, RESEED, newnextnumber)

boa sorte!

Christopher Klein
fonte
1

A modificação da identidade pode falhar, dependendo de vários fatores, principalmente em torno dos objetos / relacionamentos vinculados à coluna do ID. Parece que o design do banco de dados é um problema aqui, já que os IDs raramente devem mudar (tenho certeza de que você tem seus motivos e está cascasando as alterações). Se você realmente precisar alterar os IDs de tempos em tempos, sugiro criar uma nova coluna de ID fictícia que não seja a chave primária / número automático que você pode gerenciar por conta própria e gerar a partir dos valores atuais. Como alternativa, a ideia de Chrisotphers acima seria minha outra sugestão se você estiver com problemas para permitir a inserção de identidade.

Boa sorte

PS: não está falhando porque a ordem sequencial em que está executando está tentando atualizar um valor na lista para um item que já existe na lista de IDs. segurando os canudos, talvez adicione o número de linhas + 1; se isso funcionar, subtraia o número de linhas: -S

Curtidor
fonte
1

Se você precisar alterar os IDs ocasionalmente, provavelmente é melhor não usar uma coluna de identidade. No passado, implementamos os campos de numeração automática manualmente usando uma tabela 'Contadores' que rastreia o próximo ID para cada tabela. No IIRC, fizemos isso porque as colunas de identidade estavam causando corrupção no banco de dados no SQL2000, mas a possibilidade de alterar os IDs era ocasionalmente útil para teste.

Robin Bennett
fonte
1

Você pode inserir novas linhas com valores modificados e excluir linhas antigas. O exemplo a seguir altera o ID para ser o mesmo da chave estrangeira PersonId

SET IDENTITY_INSERT [PersonApiLogin] ON

INSERT INTO [PersonApiLogin](
       [Id]
      ,[PersonId]
      ,[ApiId]
      ,[Hash]
      ,[Password]
      ,[SoftwareKey]
      ,[LoggedIn]
      ,[LastAccess])
SELECT [PersonId]
      ,[PersonId]
      ,[ApiId]
      ,[Hash]
      ,[Password]
      ,[SoftwareKey]
      ,[LoggedIn]
      ,[LastAccess]
FROM [db304].[dbo].[PersonApiLogin]
GO

DELETE FROM [PersonApiLogin]
WHERE [PersonId] <> ID
GO
SET IDENTITY_INSERT [PersonApiLogin] OFF
GO
Tomas Kubes
fonte
1

Primeiro salve todos os IDs e altere-os programaticamente para os valores desejados, depois remova-os do banco de dados e insira-os novamente usando algo semelhante:

use [Name.Database]
go
set identity_insert [Test] ON
insert into [dbo].[Test]
           ([Id])
     VALUES
           (2)
set identity_insert [Test] OFF

Para pastilhas em massa, use:

use [Name.Database]
go
set identity_insert [Test] ON
BULK INSERT [Test]
FROM 'C:\Users\Oscar\file.csv'
WITH (FIELDTERMINATOR = ';',
      ROWTERMINATOR = '\n',
      KEEPIDENTITY)
set identity_insert [Test] OFF

Dados de amostra de file.csv:

2;
3;
4;
5;
6;

Se você não identity_insertdesativar, você receberá o seguinte erro:

Não é possível inserir um valor explícito para a coluna de identidade na tabela 'Teste' quando IDENTITY_INSERT está definido como OFF.

Ogglas
fonte
0

Eu vi um bom artigo que me ajudou no último momento. Eu estava tentando inserir algumas linhas em uma tabela que tinha coluna de identidade, mas fez isso de forma errada e teve que excluir novamente. Depois de excluir as linhas, minha coluna de identidade foi alterada. Eu estava tentando encontrar uma maneira de atualizar a coluna que foi inserida, mas - sem sorte. Então, enquanto pesquisava no google encontrei um link ..

  1. Excluídas as colunas que foram inseridas incorretamente
  2. Use inserção forçada usando on / off de identidade (explicado abaixo)

http://beyondrelational.com/modules/2/blogs/28/posts/10337/sql-server-how-do-i-insert-an-explicit-value-into-an-identity-column-how-do- i-update-the-value-of-an.aspx

Balu
fonte
1
Mas não tenho certeza se você realmente respondeu à pergunta aqui.
Andrew Barber
0

Pergunta muito boa, primeiro precisamos no IDENTITY_INSERT para a tabela específica, depois executar a consulta de inserção (deve especificar o nome da coluna).

Nota: Depois de editar a coluna de identidade, não esqueça de desativar o IDENTITY_INSERT. Se você não tiver concluído, não poderá editar a coluna de identidade para nenhuma outra tabela.

SET IDENTITY_INSERT Emp_tb_gb_Menu ON
     INSERT Emp_tb_gb_Menu(MenuID) VALUES (68)
SET IDENTITY_INSERT Emp_tb_gb_Menu OFF

http://allinworld99.blogspot.com/2016/07/how-to-edit-identity-field-in-sql.html

Asith Raj
fonte