Criei uma tabela em um BD que já existe em outro BD. Ele foi inicialmente preenchido com os dados antigos do banco de dados. O PK da tabela tinha que receber os valores que já existem nesses registros, então não poderia ser autoincremento.
Agora preciso que a nova tabela tenha seu PK como autoincremento. Mas como posso fazer isso depois que o PK já existe e tem dados?
A maneira como eu entendo sua pergunta é que você tem uma tabela existente com uma coluna que até agora foi preenchida com valores manuais e agora você deseja (1) transformar essa coluna em uma
IDENTITY
coluna e (2) certificar-se de que oIDENTITY
início do valor mais recente nas linhas existentes.Primeiro, alguns dados de teste para brincar:
O objetivo é tornar a coluna de chave primária da tabela,
id
, umaIDENTITY
coluna que começará em 21 para o próximo registro que for inserido. Para este exemplo, a colunaxyz
representa todas as outras colunas da tabela.Antes de fazer qualquer coisa, por favor, leia os avisos na parte inferior deste post.
Primeiro, caso algo dê errado:
Agora, vamos adicionar uma coluna de trabalho temporária
id_temp
e definir essa coluna para osid
valores da coluna existente:Em seguida, precisamos descartar a
id
coluna existente (você não pode simplesmente "adicionar"IDENTITY
a uma coluna existente, você precisa criar a coluna como umIDENTITY
). A chave primária também tem que ir, porque a coluna depende dela.... e adicione a coluna novamente, desta vez como um
IDENTITY
, junto com a chave primária:Aqui é onde fica interessante. Você pode habilitar
IDENTITY_INSERT
na tabela, o que significa que você pode definir manualmente os valores de umaIDENTITY
coluna ao inserir novas linhas (mas não atualizando as linhas existentes).Com esse conjunto,
DELETE
todas as linhas da tabela, mas as linhas que você está excluindo, estãoOUTPUT
na mesma tabela - mas com valores específicos para aid
coluna (da coluna de backup).Feito isso, desligue
IDENTITY_INSERT
novamente.Solte a coluna temporária que adicionamos:
E, finalmente, resemente a
IDENTITY
coluna, para que os próximos registrosid
sejam retomados após o maior número existente naid
coluna:Verificando a tabela de exemplo, o
id
número mais alto é 20.Adicione outra linha e verifique seu novo
IDENTITY
:No exemplo, a nova linha terá
id=21
. Por fim, se estiver satisfeito, confirme a transação:Importante
Esta não é uma operação trivial e traz alguns riscos dos quais você deve estar ciente.
Faça isso em um ambiente de teste dedicado. Tenha backups. :)
Eu gosto de usar
BEGIN/COMMIT TRANSACTION
porque evita que outros processos mexam com a tabela enquanto você está no meio de alterá-la, e dá a possibilidade de reverter tudo se algo der errado. No entanto, qualquer outro processo que tente acessar sua mesa antes de você confirmar sua transação acabará esperando. Isso pode ser muito ruim se você tiver uma tabela grande e/ou estiver em um ambiente de produção.OUTPUT .. INTO
não funcionará se sua tabela de destino tiver restrições de chave estrangeira ou qualquer um dos vários outros recursos que não consigo lembrar de cabeça. Em vez disso, você pode descarregar os dados em uma tabela temporária e inseri-los novamente na tabela original. Você pode usar a alternância de partição (mesmo que não use partições).Execute essas instruções uma a uma, não como um lote ou em um procedimento armazenado.
Tente pensar em outras coisas que podem depender da
id
coluna que você está descartando e recriando. Quaisquer índices terão que ser descartados e recriados (como fizemos com a chave primária). Lembre-se de criar scripts de cada índice e restrição que você precisará recriar antecipadamente.Desabilite qualquer
INSERT
eDELETE
acionadores na mesa.Se recriar a tabela for uma opção:
Se recriar a tabela é uma opção para você, tudo é muito mais simples:
id
coluna comoIDENTITY
,IDENTITY_INSERT ON
para a mesa,IDENTITY_INSERT OFF
, eUsar UPDATE, DELETE ou INSERT para mover dados pode levar muito tempo e usar recursos (IO) em dados e arquivos/discos de log. É possível evitar o preenchimento do log de transações com potencialmente muitos registros ao trabalhar em uma grande tabela: Usando a troca de partição, apenas os metadados são alterados.
Não há movimentação de dados envolvida e, portanto, isso é realizado muito rapidamente (quase instantâneo).
Tabela de amostra
A questão não mostra a tabela original DDL. O seguinte DDL será usado como exemplo nesta resposta:
Meia dúzia de IDs aleatórios fictícios de 0 a 15 são adicionados com esta consulta:
Dados de exemplo em
IdT
Nova mesa com
IDENTITY(0, 1)
O único problema
idT
é a falta doIDENTITY(0, 1)
imóvel no id. Uma nova tabela com uma estrutura semelhante eIDENTITY(0, 1)
é criada:Além de
IDENTITY(0, 1)
,idT_Switch
é idêntico aidT
.Chaves estrangeiras
As chaves estrangeiras
idT
devem ser removidas para permitir que essa técnica seja usada.Interruptor de partição
As tabelas
idT
e têm uma estrutura compatível. Em vez deidT_Switch
usar instruções e para mover linhas de ou para si , pode ser usado:DELETE
UPDATE
INSERT
idT
idT_Switch
idT
ALTER TABLE ... SWITCH
A única 'partição' de
PK_idT
(toda a tabela) é movida paraPK_idT_Switch
(e vice-versa).idT
agora contém 0 linhas eidT_Switch
contém 6 linhas.Você pode encontrar a lista completa de requisitos de compatibilidade de origem e destino aqui:
Transferindo dados com eficiência usando a alternância de partição
Observe que esse uso de
SWITCH
não requer Enterprise Edition, pois não há particionamento explícito. Uma tabela não particionada é considerada uma tabela com uma única partição do SQL Server 2005 em diante.Substituir
idT
idT
agora está vazio e inútil e pode ser descartado:idT_Switch
pode ser renomeado e substituirá aidT
tabela antiga:Chaves estrangeiras
As chaves estrangeiras podem ser adicionadas novamente à nova
idT
tabela. Qualquer outra coisa removida anteriormenteidT
para tornar as tabelas compatíveis para comutação também precisará ser refeita.Resemente
Este comando retorna 0. A tabela idT contém 6 linhas com MAX(id) = 15. DBCC CHECKIDENT ( table_name ) pode ser usado:
Como 15 é maior que 0, ele será propagado automaticamente sem procurar por MAX(id):
IDENT_CURRENT agora retorna 15 .
Testar e adicionar dados
Uma declaração simples
INSERT
:Adiciona esta linha:
A
id
coluna agora está usando a identidade e o valor recém-inserido é de fato 16 (15+1).Mais Informações
Há uma pergunta e resposta relacionadas com mais informações sobre a
SWITCH
técnica aqui:Por que a remoção da propriedade Identity em uma coluna não é compatível
Se você quiser começar com um novo valor de identidade, precisará propagar novamente sua identidade. Dê uma olhada na documentação para
CHECKIDENT
ATIVAR e DESATIVAR IDENTITY_INSERT
Se sua tabela for TABLE_A então
Se você tem uma situação como eu, então você pode fazer isso muito mais fácil
Etapa 1: obter um bom conjunto de dados, incluindo os valores do campo de chave (no meu caso, o problema foi que a chave primária não pôde ser sincronizada corretamente com duas outras tabelas)
Etapa 2: use a capacidade do SSMS para criar uma tabela de descarte e criação da que você precisa criar. Além disso, certifique-se de que a tabela NOVA tenha uma chave de identidade primária adicionada
Etapa 3: use a seguinte consulta para inserir o incremento idêntico do PK
Etapa 4: testar se as tabelas são idênticas
Último passo: Observe que a instrução insert não estava usando o campo PK.... Ele foi usado apenas para classificar os dados corretamente para a inserção :)