Por que a remoção da propriedade Identity em uma coluna não é suportada

11

Eu li que após o SQL Server 2000, a capacidade de "desidentificar" uma coluna de identidade foi removida. E que isso era "By Design" (não apenas um recurso que faltava).

Aqui está um exemplo que encontrei em um blog . Envolve a atualização das tabelas do sistema. (E essa capacidade foi removida após o SQL Server 2000.) Entendo que fazer isso através de tabelas do sistema não é uma boa ideia. Estou apenas me perguntando por que um recurso para fazer isso de outra maneira não existe.

Contornar isso vai me causar uma quantidade considerável de trabalho. (Copiando centenas de milhões de linhas para novas tabelas em um ambiente intolerante ao tempo de inatividade.)

Então, pensei em perguntar "por que".

O que mudou no Sql Server 2005 e em versões posteriores que tornaram isso uma coisa ruim? Ou sempre foi ruim e simplesmente não foi trancado?

Que "prática recomendada" (ou princípio semelhante) seria violada ao tornar uma coluna de identidade uma coluna normal novamente?

-

Atualize para responder à solicitação de "por que estou fazendo isso":
Este é um resumo de nível muito alto: vou começar a adicionar partições às minhas tabelas. (Para que eu possa arquivar / limpar dados antigos.) Tudo isso é fácil. Ocasionalmente, porém, preciso mover um registro para uma partição diferente para que não seja removido (quando uma partição é apresentada para arquivamento / exclusão). (Minha coluna de particionamento aumenta em 2 para que haja sempre espaço para mover a linha para uma partição diferente.)

Mas se a coluna de particionamento for uma coluna de identidade, eu tenho que excluir e reinserir o valor (não há como atualizar o valor de uma coluna de identidade). O que causa problemas com a replicação.

Então, eu estou querendo usar uma sequência em vez de uma coluna de identidade. Mas essa opção é muito difícil em grandes bancos de dados.

Vaccano
fonte

Respostas:

22

Sua pergunta é, essencialmente:

Por que não posso mais fazer essa coisa arriscada que nunca deveria ter me permitido fazer?

A resposta a essa pergunta é amplamente irrelevante (embora você possa ver alguns comentários da Microsoft nesses itens do Connect solicitando esta funcionalidade: # 294193 e # 252226) Para completar, minha sinopse é: A capacidade de remover a propriedade de identidade foi um efeito colateral não intencional de ter a capacidade de mexer com as tabelas do sistema em primeiro lugar. Isso não foi planejado para ser usado de várias maneiras, muitas vezes com consequências muito ruins e, portanto, foi removido. Foi um hack de tabela do sistema não documentado e sem suporte. A capacidade de alterar dados nas tabelas do sistema não foi removida porque a Microsoft não queria mais que você saísse de uma coluna como uma coluna de identidade; foi removida porque mexer nas tabelas do sistema é extremamente arriscado. Remover a propriedade IDENTITY em si não era uma remoção de recurso especificamente direcionada, e eu nunca teria confiado totalmente nessa abordagem, mesmo nos tempos antigos em que era possível.

Dito isto, que tal respondermos a essa pergunta?

Como faço para remover a propriedade IDENTITY de uma coluna com tempo de inatividade mínimo ou nenhum?

É possível fazer isso facilmente, usando ALTER TABLE ... SWITCHuma técnica que tenho certeza de que aprendi com nosso próprio Paul White nas soluções alternativas para o Connect # 252226 . Exemplo rápido, dada esta tabela simples:

CREATE TABLE dbo.Original
(
  ID INT IDENTITY(1,1) PRIMARY KEY,
  name SYSNAME
);
GO

INSERT dbo.Original(name) VALUES(N'foo'),(N'bar');
GO

SELECT * FROM dbo.Original;
GO

Resultados:

ID  name
--  ----
1   foo
2   bar

Agora, vamos criar uma tabela de sombra e alternar para ela. Em seguida, solte a tabela antiga, renomeie a nova e retome a atividade normal:

CREATE TABLE dbo.New
(
  ID INT PRIMARY KEY,
  name SYSNAME
);
GO

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
  ALTER TABLE dbo.Original SWITCH TO dbo.New;
  DROP TABLE dbo.Original;
  EXEC sys.sp_rename N'dbo.New', N'Original', 'OBJECT';
COMMIT TRANSACTION;
GO

INSERT dbo.Original(ID,name) VALUES(3,N'splunge');
UPDATE dbo.Original SET ID = 6 WHERE ID = 1;
GO

SELECT * FROM dbo.Original;
GO

Resultados:

ID  name
--  -------
2   bar
3   splunge
6   foo

Agora limpe:

DROP TABLE dbo.Original;

Esta é apenas uma operação de metadados, sem movimentação de dados e bloqueará apenas outros usuários enquanto os metadados estiverem sendo atualizados. Mas, é certo, é um exemplo muito simplista. Se você possui chaves estrangeiras ou está usando outros recursos, como replicação, Change Data Capture, Change Tracking, etc., pode ser necessário desativar ou remover alguns deles antes de fazer essa alteração (não testei todas as combinações). Para chaves estrangeiras especificamente, consulte esta dica que mostra como gerar scripts para eliminar e recriar todas as restrições de chave estrangeira (ou selecionadas).

Além disso, você precisará atualizar o código do aplicativo para não esperar que o SQL Server preencha esta coluna e verifique as instruções de inserção ou seleção que possam depender da ordem das colunas ou das colunas que eles precisam especificar. Geralmente, gostaria de receber toda a sua base de código por qualquer menção a esta tabela.

Veja também este script de Itzik Ben-Gan (fonte: este artigo antigo ) para outra maneira de lidar com isso, mas há movimentação de dados envolvida aqui, para que ele não atenda ao requisito de "tempo de inatividade mínimo ou mínimo".

Aaron Bertrand
fonte
3
Gostaria de incentivar qualquer pessoa que esteja chegando para fazer a votação dos dois itens de conexão. Quão difícil pode ser implementar um recurso ALTER COLUMN que ativa ou desativa a identidade booleana ?! Doloroso para contornar.
usr
Devo admitir que, por algum motivo, pensei que ..SWITCH..funcionava apenas para tabelas que tinham pelo menos uma partição definida.
usar o seguinte código
Só quero adicionar que SWITCHnão requer o Enterprise Edition, embora o particionamento de tabela o exija.
Dan Guzman