Saya menggunakan SQL Server 2008 Standard, yang tidak memiliki SEQUENCE
fitur.
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 bigint
kolom RowUpdateCounter
di setiap tabel.
Idenya adalah bahwa setiap kali baris dimasukkan atau diperbarui, nomor di RowUpdateCounter
kolomnya akan berubah. Nilai-nilai yang masuk ke RowUpdateCounter
kolom harus diambil dari urutan angka yang semakin meningkat. Nilai dalam RowUpdateCounter
kolom 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 RowUpdateCounter
dapat 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 |
+----+-------+------------------+
RowUpdateCounter
untuk baris dengan ID1,2
harus tetap apa adanya, karena baris ini tidak diubah.RowUpdateCounter
untuk baris dengan ID3,4
harus berubah, karena mereka diperbarui.RowUpdateCounter
untuk baris dengan ID5,6
harus diubah, karena mereka dimasukkan.RowUpdateCounter
untuk semua baris yang diubah harus lebih besar dari 4 (yang terakhirRowUpdateCounter
dari 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
SYSDATETIME
sama untuk semua baris yang terpengaruh. Proses sinkronisasi harus dapat membaca perubahan dalam batch. Misalnya, jika ukuran kumpulan adalah 3 baris, maka setelahMERGE
langkah 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 SEQUENCE
dalam SQL Server 2008, saya meniru SEQUENCE
oleh tabel khusus dengan IDENTITY
seperti 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, INSERT
pemicu pada setiap tabel dengan RowUpdateCounter
dan menghasilkan set angka yang diperlukan di sana.
Di INSERT
, UPDATE
dan MERGE
kueri yang saya tetapkan RowUpdateCounter
ke 0, yang diganti dengan nilai yang benar di pelatuk. Dalam ???
kueri di atas adalah 0
.
Ini bekerja, tetapi apakah ada solusi yang lebih mudah?
sumber
rowversion
akan 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?A
memperbarui satu baris, perubahan barisnya berubah menjadi 123,A
belum dilakukan. waktu = 2: TransaksiB
memperbarui baris lain, perubahan barisnya menjadi 124. waktu = 3:B
komit. time = 4: proses sinkronisasi berjalan dan mengambil semua baris dengan rowversion> 122, yang berarti baris hanya diperbarui olehB
. waktu = 5:A
komit. Hasil: perubahan olehA
tidak akan pernah diambil oleh proses sinkronisasi. Apakah aku salah? Mungkin beberapa penggunaan pintarMIN_ACTIVE_ROWVERSION
akan membantu?Jawaban:
Anda dapat menggunakan
ROWVERSION
kolom untuk ini.Dokumentasi menyatakan itu
Nilai-nilainya adalah
BINARY(8)
dan Anda harus menganggapnya sebagaiBINARY
bukanBIGINT
setelah setelah0x7FFFFFFFFFFFFFFF
itu0x80...
dan mulai bekerja dari-9223372036854775808
jika diperlakukan sebagai ditandatanganibigint
.Contoh lengkap bekerja di bawah ini. Mempertahankan indeks pada
ROWVERSION
kolom akan mahal jika Anda memiliki banyak pembaruan sehingga Anda mungkin ingin menguji beban kerja Anda dengan dan tanpa melihat apakah itu sepadan dengan biayanya.sumber
@@DBTS
seharusnya adaMIN_ACTIVE_ROWVERSION()
, dan jika menggunakanMIN_ACTIVE_ROWVERSION()
perbandingan<=
harus menjadi<
dan>
menjadi>=
.@@DBTS
danMIN_ACTIVE_ROWVERSION()
jika ada transaksi aktif yang tidak berkomitmen. Jika aplikasi menggunakan@@DBTS
daripadaMIN_ACTIVE_ROWVERSION
, dimungkinkan untuk kehilangan perubahan yang aktif saat sinkronisasi terjadi.Sudahkah Anda mencoba menggunakan
IDENTITY
opsi ini?Sebagai contoh:
dimana
Ini mirip dengan URUTAN di Oracle.
sumber
IDENTITY
tidak melakukan apa yang diperlukan terkait penambahan otomatis pada pembaruan dan sisipan .IDENTITY
dapat membantu.