Gestion des plages d'identité pour la réplication transactionnelle


9

J'ai remarqué que lorsque vous configurez une réplication transactionnelle, SQL Server définit la gestion des plages d'identité sur manuelle. Cela signifie que dans ma base de données d'abonnement, lorsque j'essaie d'insérer un nouvel enregistrement dans une table dont le PK est une colonne d'identité, cela me donne une erreur et me dit qu'il a essayé d'insérer un PK de "1", "2 "," 3 ", etc. Cela est dû au fait que la valeur d'identité actuelle pour toutes les colonnes d'identité sur l'abonné est réinitialisée à la valeur de départ (généralement 1) au lieu de rester à ce qu'elle était sur l'éditeur.

Je comprends pourquoi SQL Server fait cela - vous êtes censé laisser la table des abonnés en lecture seule. Cependant, mon scénario est un peu peu orthodoxe - je mets à jour mon abonné de temps en temps par le biais de la réplication, je fais une sauvegarde immédiate de cette base de données, puis je veux faire quelques mises à jour à l'abonné qui NE SERONT PAS repoussées vers l'éditeur, puis lorsque je vais mettre à jour l'abonné à nouveau, je restaure sa base de données à partir de la sauvegarde précédente et tire les dernières mises à jour. Parce que je veux faire des mises à jour pour l'abonné entre ces mises à jour («deltas temporaires» si vous voulez), j'ai besoin de la colonne d'identité pour fonctionner et ne pas réinitialiser à 1 lors de la réplication.

J'ai essayé d'activer la gestion automatique des plages d'identité lors de la configuration de ma publication, mais cela me donne simplement l'erreur suivante lorsque j'essaie d'ajouter un tableau à la publication:

Msg 21231, niveau 16, état 1, procédure sp_MSrepl_addarticle, ligne 2243
La prise en charge de la plage d'identité automatique n'est utile que pour les publications qui permettent la mise à jour des abonnés.

Existe-t-il un moyen de contourner ce problème? Je souhaite présenter cette réplication à SQL Server comme si elle était en lecture seule du côté abonné, car je ne prévois pas de faire des mises à jour qui seront repoussées vers l'éditeur , mais je veux faire des mises à jour temporaires qui sera effacé avant la prochaine réplication.

J'ai également considéré que la réplication de snapshot pourrait être une méthode plus appropriée que la réplication transactionnelle pour mon modèle d'utilisation, mais le problème est que la réplication de snapshot nécessite d'envoyer la base de données entière à chaque mise à jour; parce que je prévois de faire une sauvegarde immédiate de la base de données après la dernière réplication, je ne devrais pas avoir besoin de faire tout le transfert à chaque fois; juste les changements depuis la dernière fois.


Quelle version de SQL Server utilisez-vous? Pouvez-vous redéfinir la table?

2008 r2. Je ne vois pas comment redéfinir la table résoudrait ce problème ...
Jez

Je pensais à une solution utilisant SEQUENCE, mais ce n'est que pour SQL 2012.

2
Is there any way I can get round this problem?Vous devez définir la colonne d'identité sur NOT FOR REPLICATION à l' aide de sys.sp_identitycolumnforreplication pour SQL Server 2005 et versions ultérieures. Vous n'avez même pas besoin de resnapshot vos articles lorsque vous modifiez la colonne d'identité comme non pour la réplication. Il suffit de ne pas le faire en utilisant l'interface graphique.
Kin Shah

Il est déjà marqué comme non destiné à la réplication. C'est essentiellement le problème - SQL Server ne copie pas les informations d'identité, donc sur l'abonné, il recommence à 1.
Jez

Réponses:


3

En supposant que votre éditeur utilise une identité int qui commence à 1, vous pouvez émettre DBCC CHECKIDENT('dbo.mytable', RESEED, -2147483648) sur l'abonné. Vous pouvez ensuite utiliser la plage de -2147483648 à 0 pour conserver vos "deltas temporaires".


C'est la solution que j'ai trouvée, mais cela signifie toujours que mon code se connecte à l'éditeur et à l'abonné et synchronise les identités manuellement. J'espérais qu'il y aurait un moyen plus automatique de le faire.
Jez

Pourquoi auriez-vous besoin de synchroniser les identités manuellement? Écrivez simplement une procédure stockée sur l'abonné qui exécute checkident pour chaque table dans laquelle vous stockez les delta temporaires et exécutez-la une fois que l'instantané a fini de s'appliquer. L'agent de distribution insérera les modifications au fur et à mesure qu'elles se produisent dans la plage d'identité "réelle", et les modifications apportées directement à l'abonné seront dans la plage négative.
Liam Confrey

1

Ce que j'ai fini par faire, c'est de m'en tenir à une réplication transactionnelle basée sur l'extraction et de demander à mon programme de mettre à jour les valeurs d'identité de l'abonné pour qu'elles soient les mêmes que celles de la base de données de publication immédiatement après la synchronisation (un peu ce que j'aimerais que l'agent de distribution fasse de lui-même) ). En pseudo-code, cela ressemblait un peu à ceci:

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

Semble fonctionner correctement. Le bit HACK est dû au fait que, bien que par défaut et avec toutes mes tables, la valeur d'identité incrémente juste d'une unité, elle peut être configurée différemment, donc techniquement ici, vous devriez découvrir comment la valeur d'identité incrémente sur la table de l'éditeur et l'incrémenter la de la même façon.


0

Ma méthode préférée pour gérer cela est la suivante:

une. Arrêtez d'abord votre agent de réplication (afin de ne pas obtenir de nouvelles données dans votre base de données d'abonné)

b. Renommez votre table existante

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

c. Recréez votre table avec l'ensemble IDENTITY

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

ré. Remplissez votre table (à partir de [BackupTableName]) avec SET IDENTITY_INSERT

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

Une fois que vous avez la contrainte IDENTITY sur votre base de données, vous pouvez soit faire une réplication personnalisée (c'est-à-dire: changer votre proc de repl d'insertion en SET IDENTITY_INSERT [TableName] ON ou vous pouvez définir l'indicateur NOT FOR REPLICATION sur la table (qui indique au serveur SQL que si l'utilisateur qui se connecte est l'agent de réplication, attendez-vous à ce que la valeur IDENTITY soit fournie) ( je préfère l'approche de réplication personnalisée, car elle me donne plus de flexibilité )

e. Modifiez votre procédure stockée de réplication d'insertion (généralement nommée sp_MSins_CurrentTable) pour également insérer à l'aide deSET 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. Vous pouvez maintenant redémarrer votre agent de réplication.


1
lol, par rapport à l'utilisation DBCC CHECKIDENT, cette méthode est une énorme quantité de travail.
Jez

@Jez vous devrez recréer la table (avec IDENTITY) afin d'exécuter le DBCC CHECKIDENT ... Un instantané de la réplication créera la table sans la contrainte IDENTITY (en fonction de votre q je dirais que le DBCC CHECKIDENT a gagné ne marche pas)
Andrew Bickerton

Pour info, cela a fonctionné et la réplication crée la table avec la contrainte IDENTITY ...
Jez

@Jez quel type de réplication avez-vous configuré? (si vous le configurez en tant que MERGE, cela se produira, pour TRANSACTIONNEL, ce n'est généralement pas le cas, bien que la réplication soit hautement personnalisable si vous n'utilisez pas l'interface graphique)
Andrew Bickerton

Transactionnel. Comme je l'ai dit, l'IDENTITÉ est là, mais la valeur d'identité actuelle est réinitialisée à la valeur de départ (1).
Jez
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.