Saya menggunakan SQL Server 2008 Standard, yang tidak memiliki SEQUENCEfitur.
Sistem eksternal membaca data dari beberapa tabel khusus pada basis data utama. Sistem eksternal menyimpan salinan data dan secara berkala memeriksa perubahan dalam data dan menyegarkan salinannya.
Untuk membuat sinkronisasi efisien, saya ingin mentransfer hanya baris yang diperbarui atau disisipkan sejak sinkronisasi sebelumnya. (Baris tidak pernah dihapus). Untuk mengetahui baris mana yang diperbarui atau disisipkan sejak sinkronisasi terakhir ada bigintkolom RowUpdateCounterdi setiap tabel.
Idenya adalah bahwa setiap kali baris dimasukkan atau diperbarui, nomor di RowUpdateCounterkolomnya akan berubah. Nilai-nilai yang masuk ke RowUpdateCounterkolom harus diambil dari urutan angka yang semakin meningkat. Nilai dalam RowUpdateCounterkolom harus unik dan setiap nilai baru yang disimpan dalam tabel harus lebih besar dari nilai sebelumnya.
Silakan lihat skrip yang menunjukkan perilaku yang diinginkan.
Skema
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
Masukkan beberapa baris
INSERT INTO [dbo].[Test]
([ID]
,[Value]
,[RowUpdateCounter])
VALUES
(1, 'A', ???),
(2, 'B', ???),
(3, 'C', ???),
(4, 'D', ???);
Hasil yang diharapkan
+----+-------+------------------+
| ID | Value | RowUpdateCounter |
+----+-------+------------------+
| 1 | A | 1 |
| 2 | B | 2 |
| 3 | C | 3 |
| 4 | D | 4 |
+----+-------+------------------+
Nilai yang dihasilkan di RowUpdateCounterdapat berbeda, katakanlah 5, 3, 7, 9,. Mereka harus unik dan mereka harus lebih besar dari 0, karena kita mulai dari tabel kosong.
Masukkan dan perbarui beberapa baris
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
,???)
;
Hasil yang diharapkan
+----+-------+------------------+
| ID | Value | RowUpdateCounter |
+----+-------+------------------+
| 1 | A | 1 |
| 2 | B | 2 |
| 3 | E | 5 |
| 4 | F | 6 |
| 5 | G | 7 |
| 6 | H | 8 |
+----+-------+------------------+
RowUpdateCounteruntuk baris dengan ID1,2harus tetap apa adanya, karena baris ini tidak diubah.RowUpdateCounteruntuk baris dengan ID3,4harus berubah, karena mereka diperbarui.RowUpdateCounteruntuk baris dengan ID5,6harus diubah, karena mereka dimasukkan.RowUpdateCounteruntuk semua baris yang diubah harus lebih besar dari 4 (yang terakhirRowUpdateCounterdari urutan).
Urutan di mana nilai-nilai baru ( 5,6,7,8) ditugaskan ke baris yang diubah tidak terlalu penting. Nilai-nilai baru dapat memiliki kesenjangan, misalnya 15,26,47,58, tetapi tidak boleh menurun.
Ada beberapa tabel dengan penghitung seperti itu dalam database. Tidak masalah jika mereka semua menggunakan urutan global tunggal untuk nomor mereka, atau setiap tabel memiliki urutan masing-masing.
Saya tidak ingin menggunakan kolom dengan cap datetime alih-alih penghitung bilangan bulat, karena:
Jam di server dapat melompat maju dan mundur. Terutama ketika itu pada mesin virtual.
Nilai yang dikembalikan oleh fungsi sistem seperti
SYSDATETIMEsama untuk semua baris yang terpengaruh. Proses sinkronisasi harus dapat membaca perubahan dalam batch. Misalnya, jika ukuran kumpulan adalah 3 baris, maka setelahMERGElangkah di atas proses sinkronisasi hanya akan membaca barisE,F,G. Ketika proses sinkronisasi dijalankan lain kali akan melanjutkan dari barisH.
Cara saya melakukannya sekarang agak jelek.
Karena tidak ada SEQUENCEdalam SQL Server 2008, saya meniru SEQUENCEoleh tabel khusus dengan IDENTITYseperti yang ditunjukkan pada jawaban ini . Ini sendiri cukup jelek dan diperburuk oleh kenyataan bahwa saya perlu menghasilkan tidak satu, tetapi batch angka sekaligus.
Kemudian, saya memiliki INSTEAD OF UPDATE, INSERTpemicu pada setiap tabel dengan RowUpdateCounterdan menghasilkan set angka yang diperlukan di sana.
Di INSERT, UPDATEdan MERGEkueri yang saya tetapkan RowUpdateCounterke 0, yang diganti dengan nilai yang benar di pelatuk. Dalam ???kueri di atas adalah 0.
Ini bekerja, tetapi apakah ada solusi yang lebih mudah?
rowversionakan memberi saya kemungkinan ini, jika saya benar memahami apa itu ... Apakah dijamin akan semakin meningkat?
rowversion. Terlihat sangat menggoda. Satu-satunya kekhawatiran saya adalah bahwa semua contoh penggunaannya yang telah saya lihat sejauh ini berputar di sekitar mendeteksi apakah satu baris berubah. Saya perlu cara yang efisien untuk mengetahui apa set baris berubah sejak saat tertentu. Selain itu, apakah mungkin untuk melewatkan pembaruan?
Amemperbarui satu baris, perubahan barisnya berubah menjadi 123, Abelum dilakukan. waktu = 2: Transaksi Bmemperbarui baris lain, perubahan barisnya menjadi 124. waktu = 3: Bkomit. time = 4: proses sinkronisasi berjalan dan mengambil semua baris dengan rowversion> 122, yang berarti baris hanya diperbarui oleh B. waktu = 5: Akomit. Hasil: perubahan oleh Atidak akan pernah diambil oleh proses sinkronisasi. Apakah aku salah? Mungkin beberapa penggunaan pintar MIN_ACTIVE_ROWVERSIONakan membantu?