SEQUENCE
機能のないSQL Server 2008 Standardを使用しています。
外部システムは、メインデータベースのいくつかの専用テーブルからデータを読み取ります。外部システムはデータのコピーを保持し、データの変更を定期的にチェックして、そのコピーを更新します。
同期を効率的にするために、前回の同期以降に更新または挿入された行のみを転送したいと思います。(行が削除されることはありません)。前回の同期以降に更新または挿入された行を知るために、各テーブルにbigint
列RowUpdateCounter
があります。
行が挿入または更新されるたびに、そのRowUpdateCounter
列の数が変化するという考え方です。RowUpdateCounter
列に入る値は、増え続ける数列から取得する必要があります。RowUpdateCounter
列の値は一意である必要があり、テーブルに格納される各新しい値は、以前のどの値よりも大きくなければなりません。
望ましい動作を示すスクリプトをご覧ください。
スキーマ
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
いくつかの行を挿入
INSERT INTO [dbo].[Test]
([ID]
,[Value]
,[RowUpdateCounter])
VALUES
(1, 'A', ???),
(2, 'B', ???),
(3, 'C', ???),
(4, 'D', ???);
期待される結果
+----+-------+------------------+
| ID | Value | RowUpdateCounter |
+----+-------+------------------+
| 1 | A | 1 |
| 2 | B | 2 |
| 3 | C | 3 |
| 4 | D | 4 |
+----+-------+------------------+
で生成される値はRowUpdateCounter
、たとえばとは異なる場合があり5, 3, 7, 9
ます。空のテーブルから始めたため、一意であり、0より大きい必要があります。
一部の行のINSERTおよびUPDATE
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
,???)
;
期待される結果
+----+-------+------------------+
| ID | Value | RowUpdateCounter |
+----+-------+------------------+
| 1 | A | 1 |
| 2 | B | 2 |
| 3 | E | 5 |
| 4 | F | 6 |
| 5 | G | 7 |
| 6 | H | 8 |
+----+-------+------------------+
RowUpdateCounter
1,2
これらの行は変更されなかったため、IDのある行はそのままにしておく必要があります。RowUpdateCounter
IDの行3,4
は更新されたため、変更する必要があります。RowUpdateCounter
5,6
挿入されたため、IDのある行は変更されます。RowUpdateCounter
すべての変更された行は4(RowUpdateCounter
シーケンスの最後)より大きい必要があります。
5,6,7,8
変更された行に新しい値()が割り当てられる順序は、実際には重要ではありません。新しい値にはギャップなどがありますが、15,26,47,58
減少することはありません。
データベースには、このようなカウンターを持つテーブルがいくつかあります。それらのすべてが番号に単一のグローバルシーケンスを使用するか、各テーブルに独自のシーケンスがあるかは問題ではありません。
次の理由により、整数カウンターの代わりに日時スタンプの列を使用したくありません。
サーバーのクロックは、順方向と逆方向の両方にジャンプできます。特に仮想マシン上にある場合。
のようなシステム関数によって返される値は、
SYSDATETIME
影響を受けるすべての行で同じです。同期プロセスは、変更をバッチで読み取ることができる必要があります。たとえば、バッチサイズが3行の場合、MERGE
上記の手順の後、同期プロセスは行のみを読み取りますE,F,G
。同期プロセスが次回実行されるときに、行から続行されH
ます。
私が今やっていることはかなり醜いです。
SEQUENCE
SQL Server 2008 にはないので、この回答に示すようにSEQUENCE
、専用のテーブルでをエミュレートします。これ自体はかなり醜く、一度に1つの数値ではなくバッチの数値を生成する必要があるという事実により、さらに悪化します。IDENTITY
次に、を使用してINSTEAD OF UPDATE, INSERT
各テーブルにトリガーをRowUpdateCounter
設定し、そこに必要な数のセットを生成します。
でINSERT
、UPDATE
およびMERGE
Iセットを照会RowUpdateCounter
トリガーに正しい値で置換され、0。???
上記のクエリではあります0
。
それは機能しますが、より簡単な解決策はありますか?
rowversion
私が正しくそれが何であるかを理解していれば、私はこの可能性を明らかにしなかった...それはますます増加することが保証されていますか?
rowversion
。とても魅力的に見えます。私の唯一の懸念は、これまでに見たその使用例はすべて、単一の行が変更されたかどうかを検出することに関係しています。特定の瞬間以降に変更された行のセットを効率的に知る方法が必要です。また、アップデートを見逃す可能性はありますか?
A
は行を更新し、行バージョンは123に変更されますが、A
まだコミットされていません。time = 2:トランザクションはB
別の行を更新し、その行バージョンは124に変更されます。time= 3:B
コミットします。time = 4:同期プロセスが実行され、rowversion> 122のすべての行がフェッチされB
ます。つまり、によってのみ行が更新されます。time = 5:A
コミット。結果:による変更A
は、同期プロセスによって取得されません。私が間違っている?多分いくつかの巧妙な使用MIN_ACTIVE_ROWVERSION
が役立ちますか?