PRIMEIRO
Você provavelmente não precisa de todas as três colunas: old_id
, external_id
, new_id
. A new_id
coluna, sendo um IDENTITY
, terá um novo valor gerado para cada linha, mesmo quando você inserir external_id
. Mas, entre old_id
e external_id
, esses são praticamente mutuamente exclusivos: ou já existe um old_id
valor ou essa coluna, na concepção atual, será apenas se NULL
estiver usando external_id
ou new_id
. Como você não adicionará um novo ID "externo" a uma linha que já existe (ou seja, uma que tenha um old_id
valor), e não haverá novos valores old_id
, então pode haver uma coluna usada para os dois propósitos.
Portanto, livre-se da external_id
coluna e renomeie old_id
para algo parecido old_or_external_id
ou o que for. Isso não deve exigir nenhuma alteração real em nada, mas reduz algumas das complicações. No máximo, pode ser necessário chamar a coluna external_id
, mesmo que contenha valores "antigos", se o código do aplicativo já estiver gravado para inserção external_id
.
Isso reduz a nova estrutura a ser justa:
PkId AS AS COALESCE(old_or_external_id, new_id, -1) PERSISTED NOT NULL,
old_or_external_id INT NULL, -- values from existing record OR passed in from app
new_id INT IDENTITY(2000000, 1) NOT NULL
Agora você adicionou apenas 8 bytes por linha em vez de 12 bytes (supondo que você não esteja usando a SPARSE
opção ou Compactação de dados). E você não precisou alterar nenhum código, T-SQL ou código do aplicativo.
SEGUNDO
Continuando nesse caminho de simplificação, vejamos o que nos resta:
- A
old_or_external_id
coluna já possui valores ou receberá um novo valor do aplicativo ou será deixada como NULL
.
- O
new_id
sempre terá um novo valor gerado, mas esse valor será usado apenas se a old_or_external_id
coluna for NULL
.
Nunca há um momento em que você precisaria de valores em ambos old_or_external_id
e new_id
. Sim, haverá momentos em que as duas colunas terão valores devido a new_id
serem um IDENTITY
, mas esses new_id
valores serão ignorados. Novamente, esses dois campos são mutuamente exclusivos. E agora?
Agora podemos analisar por que precisamos disso external_id
em primeiro lugar. Considerando que é possível inserir em uma IDENTITY
coluna usando SET IDENTITY_INSERT {table_name} ON;
, você pode evitar alterações no esquema e modificar apenas o código do aplicativo para agrupar as INSERT
instruções / operações SET IDENTITY_INSERT {table_name} ON;
e SET IDENTITY_INSERT {table_name} OFF;
instruções. Você precisa determinar em qual intervalo inicial redefinir a IDENTITY
coluna (para valores recém-gerados), pois precisará estar bem acima dos valores que o código do aplicativo será inserido, pois a inserção de um valor mais alto fará com que o próximo valor gerado automaticamente seja ser maior que o valor MAX atual. Mas você sempre pode inserir um valor abaixo do valor IDENT_CURRENT .
A combinação das colunas old_or_external_id
e new_id
também não aumenta as chances de ocorrer uma sobreposição de valores entre valores gerados automaticamente e valores gerados por aplicativos, uma vez que a intenção de ter as colunas 2 ou 3 é combiná-las em um valor de Chave Primária, e esses são sempre valores únicos.
Nesta abordagem, você só precisa:
Deixe as tabelas como estão:
PkId INT IDENTITY(1,1) PRIMARY KEY
Isso adiciona 0 bytes a cada linha, em vez de 8 ou até 12.
- Determine o intervalo inicial para valores gerados por aplicativos. Eles serão maiores que o valor MAX atual em cada tabela, mas menores que o que se tornará o valor mínimo para os valores gerados automaticamente.
- Determine em qual valor o intervalo gerado automaticamente deve começar. Deve haver muito espaço entre o atual valor MAX e muito espaço para crescer, sabendo que no limite superior é pouco mais de 2,14 bilhões. Você pode definir esse novo valor mínimo de propagação via DBCC CHECKIDENT .
- Coloque o código do aplicativo INSERTs
SET IDENTITY_INSERT {table_name} ON;
e as SET IDENTITY_INSERT {table_name} OFF;
instruções.
SEGUNDA, parte B
Uma variação na abordagem observada diretamente acima seria fazer com que o código do aplicativo insira valores começando com -1 e diminuindo a partir daí. Isso deixa os IDENTITY
valores como os únicos subindo . O benefício aqui é que você não apenas não complica o esquema, mas também não precisa se preocupar em encontrar IDs sobrepostos (se os valores gerados pelo aplicativo forem executados no novo intervalo gerado automaticamente). Essa é apenas uma opção se você ainda não estiver usando valores negativos de ID (e parece muito raro as pessoas usarem valores negativos em colunas geradas automaticamente, portanto, essa deve ser uma possibilidade provável na maioria das situações).
Nesta abordagem, você só precisa:
Deixe as tabelas como estão:
PkId INT IDENTITY(1,1) PRIMARY KEY
Isso adiciona 0 bytes a cada linha, em vez de 8 ou até 12.
- O intervalo inicial para os valores gerados pelo aplicativo será
-1
.
- Coloque o código do aplicativo INSERTs
SET IDENTITY_INSERT {table_name} ON;
e as SET IDENTITY_INSERT {table_name} OFF;
instruções.
Aqui você ainda precisa fazer o IDENTITY_INSERT
, mas: você não adiciona nenhuma nova coluna, não precisa "reimplementar" nenhuma IDENTITY
coluna e não tem risco futuro de sobreposições.
SEGUNDA, Parte 3
Uma última variação dessa abordagem seria possivelmente trocar as IDENTITY
colunas e, em vez disso, usar Sequências . O motivo para adotar essa abordagem é poder fazer com que o código do aplicativo insira valores que sejam: positivos, acima do intervalo gerado automaticamente (não abaixo) e sem necessidade SET IDENTITY_INSERT ON / OFF
.
Nesta abordagem, você só precisa:
- Crie sequências usando CREATE SEQUENCE
Copie a IDENTITY
coluna para uma nova coluna que não possui a IDENTITY
propriedade, mas possui uma DEFAULT
restrição usando a função NEXT VALUE FOR :
PkId INT PRIMARY KEY CONSTRAINT [DF_TableName_NextID] DEFAULT (NEXT VALUE FOR...)
Isso adiciona 0 bytes a cada linha, em vez de 8 ou até 12.
- O intervalo inicial para valores gerados por aplicativos estará bem acima do que você acha que os valores gerados automaticamente se aproximarão.
- Coloque o código do aplicativo INSERTs
SET IDENTITY_INSERT {table_name} ON;
e as SET IDENTITY_INSERT {table_name} OFF;
instruções.
NO ENTANTO , devido ao requisito de que o código com SCOPE_IDENTITY()
ou @@IDENTITY
ainda funcione corretamente, alternar para Sequências atualmente não é uma opção, pois parece que não há equivalente dessas funções para Sequências :-(. Triste!