Lidando com intervalos de identidade para replicação transacional

9

Percebi que, quando você configura uma replicação transacional, o SQL Server define o gerenciamento do intervalo de identidades como manual. O que isso significa é que, no meu banco de dados de inscrição, quando tento inserir um novo registro em uma tabela cuja PK é uma coluna de identidade, ele gera um erro e diz que tentou inserir uma PK de "1", "2 "," 3 "etc. etc. Isso ocorre porque o valor atual da identidade de todas as colunas de identidade no assinante é redefinido para o valor inicial (geralmente 1) em vez de permanecer no que era no editor.

Entendo por que o SQL Server faz isso - você deve deixar a tabela de assinantes como somente leitura. No entanto, meu cenário é um pouco ortodoxo - eu atualizo meu assinante periodicamente através da replicação, faço um backup imediato desse banco de dados e desejo fazer algumas atualizações no assinante que NÃO serão enviadas de volta ao editor e, em seguida, Quando vou atualizar o assinante novamente, restauro o banco de dados do backup anterior e retiro as atualizações mais recentes. Como desejo fazer atualizações para o assinante entre essas atualizações ('deltas temporários', se desejar), preciso que a coluna de identidade funcione e não redefina para 1 quando replicada.

Tentei ativar o gerenciamento automático de intervalo de identidades ao configurar minha publicação, mas isso só me dá o seguinte erro ao tentar adicionar uma tabela à publicação:

Msg 21231, Nível 16, Estado 1, Procedimento sp_MSrepl_addarticle, Linha 2243
O suporte automático ao intervalo de identidade é útil apenas para publicações que permitem a atualização de assinantes.

Existe alguma maneira de contornar esse problema? Eu quero apresentar essa replicação no SQL Server como se fosse somente leitura no final do assinante, porque não pretendo fazer atualizações que serão enviadas de volta ao editor , mas quero fazer atualizações temporárias que será apagado antes da próxima replicação.

Também considerei que a replicação de instantâneo pode ser um método mais apropriado do que a replicação transacional para o meu padrão de uso, mas o problema é que a replicação de instantâneo exige o envio de todo o maldito banco de dados a cada atualização; porque estou planejando fazer um backup imediato do banco de dados após a última replicação, não preciso fazer essa transferência toda vez; apenas as mudanças desde a última vez.

Jez
fonte
Qual versão do SQL Server você está usando? Você pode redefinir a tabela?
2008 r2. Eu não vejo como a redefinição da mesa iria resolver este problema ...
Jez
Eu estava pensando em uma solução utilizando uma sequência, mas isso é apenas para SQL 2012.
2
Is there any way I can get round this problem?Você deve definir a coluna de identidade como NÃO PARA REPLICAÇÃO usando sys.sp_identitycolumnforreplication para sql server 2005 e versões posteriores . Você nem precisa refazer uma captura instantânea de seus artigos quando altera a coluna de identidade para não ser replicada. Apenas não faça isso usando GUI.
Kin Shah
Já está marcado como não para replicação. Isso é basicamente o problema - SQL Server não copiar a informação de identidade para o assinante, ele começa de novo em 1.
Jez

Respostas:

3

Supondo que seu Publicador esteja usando uma identidade int que começa em 1, você pode emitir DBCC CHECKIDENT('dbo.mytable', RESEED, -2147483648) o assinante. Você pode usar o intervalo de -2147483648 a 0 para manter seus "deltas temporários".

Liam Confrey
fonte
Essa é a solução que eu criei, mas ainda significa que meu código está conectado ao editor e ao assinante e sincronizando as identidades manualmente. Eu esperava que houvesse uma maneira mais automática de fazer isso.
Jez
Por que você precisaria sincronizar as identidades manualmente? Basta escrever um procedimento armazenado no assinante que executa o checkident para cada tabela em que você está armazenando delta temporário e execute-o após o término da aplicação do instantâneo. O agente de distribuição inserirá as alterações conforme elas ocorrerem no intervalo de identidade "real" e as alterações feitas diretamente no assinante estarão no intervalo negativo.
Liam Confrey
1

O que acabei fazendo foi ficar com uma replicação transacional baseada em pull e fazer com que meu programa atualizasse os valores de identidade do assinante para serem os mesmos do banco de dados de publicação imediatamente após a sincronização (meio que eu gostaria que o agente de distribuição fizesse por conta própria ) No pseudo-código, parecia um pouco com isso:

synchronize databases with TransSynchronizationAgent

equivalentTablesNotFound is a list of strings
for each table in publisher tables:
    try:
        check table identity value (this is via functionality provided by .NET's Microsoft.SqlServer.Management.Smo.Server class)
        parse identity value as integer to newIdentity
        if the table's identity value was NULL, skip to next loop iteration
        (HACK) increment newIdentity value by 1
        if there is no subscriber table with the same name as this one:
            record its name in equivalentTablesNotFound and skip to next loop iteration
        set subscriber table with same name's identity value to newIdentity using TSQL: DBCC CHECKIDENT ("tableName", newIdentity)
    catch:
        if exception shows that the error was because the table doesn't have an identity column, drop the exception

if equivalentTablesNotFound has more than zero entries, warn about tables on publisher without an equivalent name on subscriber

Parece funcionar bem. O bit HACK é porque, embora por padrão e com todas as minhas tabelas, o valor da identidade apenas aumente em uma, ele pode ser configurado de maneira diferente; portanto, aqui tecnicamente você deve descobrir como o valor da identidade é incrementado na tabela do editor e incrementado o mesma maneira.

Jez
fonte
0

Meu método preferido para lidar com isso é fazer o seguinte:

uma. Primeiro, pare o agente de replicação (para não receber novos dados no banco de dados do assinante)

b. Segundo renomeie sua tabela existente

exec sp_rename '[CurrentTable]', '[BackupTableName]'

c. Recrie sua tabela com o conjunto de IDENTITY

CREATE TABLE [CurrentTable]
(
   ID INT NOT NULL IDENTITY(1,1), 
   OtherField VARCHAR(10) NULL,
   ....
)

d. Preencha sua tabela (a partir de [BackupTableName]) com SET IDENTITY_INSERT

SET IDENTITY_INSERT [CurrentTable] ON
INSERT INTO [CurrentTable] (ID, OtherField, ...)
SELECT ID, OtherField, ....
FROM [BackupTableName]
SET IDENTITY_INSERT [CurrentTable] OFF

Depois de ter a restrição IDENTITY no seu banco de dados, você pode fazer a replicação personalizada (por exemplo: alterar o processo de inserção de inserção para SET IDENTITY_INSERT [TableName] ON ou definir o sinalizador NOT FOR REPLICATION na tabela (que informa ao SQL Server que se o usuário que está se conectando for o agente de replicação, espere que o valor de IDENTITY seja fornecido) ( prefiro a abordagem de replicação personalizada, pois ela me oferece mais flexibilidade )

e Modifique o procedimento armazenado de replicação de inserção (geralmente chamado sp_MSins_CurrentTable) para também inserir usandoSET IDENTITY INSERT

ALTER procedure [dbo].[sp_MSins_CurrentTable]
    @c1 int, @c2 varchar(50), ...
as
begin
    /* allow replication to insert values for IDENTITY */
    SET IDENTITY_INSERT [CurrentTable] ON
    insert into [CurrentTable]
        ([ID], [OtherField], ...)
    values
        (@c1, @c2, ...)
    /* now turn off Identity insert */
    SET IDENTITY_INSERT [CurrentTable] OFF
end

f. Agora você pode reiniciar seu agente de replicação.

Andrew Bickerton
fonte
11
lol, comparado ao uso DBCC CHECKIDENT, esse método é uma quantidade enorme de trabalho.
Jez
@Jez, você precisará recriar a tabela (com IDENTITY) para executar o DBCC CHECKIDENT ... Um instantâneo de replicação criará a tabela sem a restrição de IDENTITY (com base no seu q eu diria que o DBCC CHECKIDENT ganhou trabalho)
Andrew Bickerton
FYI fez trabalho ea replicação cria a tabela com a restrição de identidade ...
Jez
@ Jez, que tipo de replicação você configurou? (se você configurá-lo como MERGE que vai acontecer, por TRANSACTIONAL, geralmente, não, embora a replicação é altamente personalizável, se você não usar o GUI)
Andrew Bickerton
Transacional. Como eu disse, a IDENTITY está lá, mas o valor atual da identidade é redefinido para o valor inicial (1).
Jez