J'utilise SQL Server 2008 Standard, qui n'a pas de SEQUENCE
fonctionnalité.
Un système externe lit les données de plusieurs tables dédiées de la base de données principale. Le système externe conserve une copie des données et vérifie périodiquement les modifications des données et actualise sa copie.
Pour rendre la synchronisation efficace, je souhaite transférer uniquement les lignes qui ont été mises à jour ou insérées depuis la synchronisation précédente. (Les lignes ne sont jamais supprimées). Pour savoir quelles lignes ont été mises à jour ou insérées depuis la dernière synchronisation, il y a une bigint
colonne RowUpdateCounter
dans chaque table.
L'idée est qu'à chaque fois qu'une ligne est insérée ou mise à jour, le nombre dans sa RowUpdateCounter
colonne change. Les valeurs qui entrent dans la RowUpdateCounter
colonne doivent être tirées d'une séquence de nombres toujours croissante. Les valeurs de la RowUpdateCounter
colonne doivent être uniques et chaque nouvelle valeur stockée dans une table doit être supérieure à toute valeur précédente.
Veuillez consulter les scripts qui montrent le comportement souhaité.
Schéma
CREATE TABLE [dbo].[Test](
[ID] [int] NOT NULL,
[Value] [varchar](50) NOT NULL,
[RowUpdateCounter] [bigint] NOT NULL,
CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED
(
[ID] ASC
))
GO
CREATE UNIQUE NONCLUSTERED INDEX [IX_RowUpdateCounter] ON [dbo].[Test]
(
[RowUpdateCounter] ASC
)
GO
INSÉRER quelques lignes
INSERT INTO [dbo].[Test]
([ID]
,[Value]
,[RowUpdateCounter])
VALUES
(1, 'A', ???),
(2, 'B', ???),
(3, 'C', ???),
(4, 'D', ???);
Résultat attendu
+----+-------+------------------+
| ID | Value | RowUpdateCounter |
+----+-------+------------------+
| 1 | A | 1 |
| 2 | B | 2 |
| 3 | C | 3 |
| 4 | D | 4 |
+----+-------+------------------+
Les valeurs générées dans RowUpdateCounter
peuvent être différentes, par exemple 5, 3, 7, 9
. Ils doivent être uniques et supérieurs à 0, car nous sommes partis d'une table vide.
INSÉRER et METTRE À JOUR certaines lignes
DECLARE @NewValues TABLE (ID int NOT NULL, Value varchar(50));
INSERT INTO @NewValues (ID, Value) VALUES
(3, 'E'),
(4, 'F'),
(5, 'G'),
(6, 'H');
MERGE INTO dbo.Test WITH (HOLDLOCK) AS Dst
USING
(
SELECT ID, Value
FROM @NewValues
)
AS Src ON Dst.ID = Src.ID
WHEN MATCHED THEN
UPDATE SET
Dst.Value = Src.Value
,Dst.RowUpdateCounter = ???
WHEN NOT MATCHED BY TARGET THEN
INSERT
(ID
,Value
,RowUpdateCounter)
VALUES
(Src.ID
,Src.Value
,???)
;
Résultat attendu
+----+-------+------------------+
| ID | Value | RowUpdateCounter |
+----+-------+------------------+
| 1 | A | 1 |
| 2 | B | 2 |
| 3 | E | 5 |
| 4 | F | 6 |
| 5 | G | 7 |
| 6 | H | 8 |
+----+-------+------------------+
RowUpdateCounter
pour les lignes avec ID1,2
doivent rester telles quelles, car ces lignes n'ont pas été modifiées.RowUpdateCounter
pour les lignes avec ID3,4
doit changer, car elles ont été mises à jour.RowUpdateCounter
pour les lignes avec ID5,6
doit changer, car elles ont été insérées.RowUpdateCounter
pour toutes les lignes modifiées doit être supérieur à 4 (le dernierRowUpdateCounter
de la séquence).
L'ordre dans lequel les nouvelles valeurs ( 5,6,7,8
) sont attribuées aux lignes modifiées n'a pas vraiment d'importance. Les nouvelles valeurs peuvent présenter des lacunes, par exemple 15,26,47,58
, mais elles ne devraient jamais diminuer.
Il existe plusieurs tables avec de tels compteurs dans la base de données. Peu importe si tous utilisent la seule séquence globale pour leurs numéros, ou si chaque table a sa propre séquence individuelle.
Je ne veux pas utiliser une colonne avec un tampon datetime au lieu d'un compteur entier, car:
L'horloge du serveur peut sauter en avant et en arrière. Surtout quand il est sur une machine virtuelle.
Les valeurs renvoyées par les fonctions système comme
SYSDATETIME
sont les mêmes pour toutes les lignes affectées. Le processus de synchronisation doit pouvoir lire les modifications des lots. Par exemple, si la taille du lot est de 3 lignes, après l'MERGE
étape ci-dessus, le processus de synchronisation ne lira que les lignesE,F,G
. Lorsque le processus de synchronisation est exécuté la prochaine fois, il continuera à partir de la ligneH
.
La façon dont je le fais maintenant est plutôt moche.
Puisqu'il n'y en a pas SEQUENCE
dans SQL Server 2008, j'émule le SEQUENCE
par une table dédiée avec IDENTITY
comme indiqué dans cette réponse . Cela en soi est assez moche et exacerbé par le fait que je dois générer non pas un seul, mais un lot de nombres à la fois.
Ensuite, j'ai un INSTEAD OF UPDATE, INSERT
déclencheur sur chaque table avec RowUpdateCounter
et j'y génère les ensembles de nombres requis.
Dans les requêtes INSERT
, UPDATE
et MERGE
j'ai défini RowUpdateCounter
0, qui est remplacé par les valeurs correctes dans le déclencheur. Les ???
dans les requêtes ci-dessus sont 0
.
Cela fonctionne, mais existe-t-il une solution plus simple?
la source
rowversion
ne me donnerait pas cette possibilité, si je comprends bien ce qu'il est ... Est - il sûr d'être de plus en plus?rowversion
. Ça a l'air très tentant. Ma seule préoccupation est que tous les exemples d'utilisation que j'ai vus jusqu'à présent tournent autour de la détection d'une modification d'une seule ligne. J'ai besoin d'un moyen efficace de savoir quel ensemble de lignes a changé depuis un certain moment. D'ailleurs, est-il possible de manquer une mise à jour?A
met à jour une ligne, sa version de ligne passe à 123,A
n'est pas encore validée . time = 2: la transactionB
met à jour une autre ligne, sa version de ligne passe à 124. time = 3:B
valide. time = 4: le processus de synchronisation s'exécute et récupère toutes les lignes avec rowversion> 122, ce qui signifie que les lignes sont mises à jour uniquement parB
. time = 5:A
valide. Résultat: les modifications apportées parA
ne seront jamais détectées par le processus de synchronisation. Ai-je tort? Peut-être qu'une utilisation intelligente deMIN_ACTIVE_ROWVERSION
sera utile?Réponses:
Vous pouvez utiliser une
ROWVERSION
colonne pour cela.La documentation indique que
Les valeurs sont
BINARY(8)
et vous devez les considérer commeBINARY
plutôt queBIGINT
comme après0x7FFFFFFFFFFFFFFF
qu'elles ont0x80...
commencé et qu'elles commencent à fonctionner-9223372036854775808
si elles sont traitées comme signéesbigint
.Un exemple complet de travail est ci-dessous. Le maintien de l'index sur la
ROWVERSION
colonne coûtera cher si vous avez beaucoup de mises à jour, vous voudrez peut-être tester votre charge de travail avec et sans pour voir si elle en vaut le coût.la source
@@DBTS
il devrait y avoirMIN_ACTIVE_ROWVERSION()
, et si vous utilisez laMIN_ACTIVE_ROWVERSION()
comparaison<=
devrait devenir<
et>
devenir>=
.@@DBTS
etMIN_ACTIVE_ROWVERSION()
s'il y a des transactions non engagées actives. Si une application utilise@@DBTS
plutôt queMIN_ACTIVE_ROWVERSION
, il est possible d'omettre les modifications actives lors de la synchronisation.Avez-vous essayé d'utiliser l'
IDENTITY
option?Par exemple:
où
Ceci est similaire à SEQUENCE dans Oracle.
la source
IDENTITY
ne fait pas ce qui est requis pour l'incrémentation automatique des mises à jour et des insertions .IDENTITY
peut aider.