Tăng bộ đếm cho mỗi hàng thay đổi


8

Tôi đang sử dụng SQL Server 2008 Standard, không có SEQUENCEtính năng này.

Một hệ thống bên ngoài đọc dữ liệu từ một số bảng chuyên dụng của cơ sở dữ liệu chính. Hệ thống bên ngoài giữ một bản sao dữ liệu và kiểm tra định kỳ các thay đổi trong dữ liệu và làm mới bản sao của nó.

Để thực hiện đồng bộ hóa hiệu quả, tôi muốn chỉ chuyển các hàng đã được cập nhật hoặc chèn kể từ đồng bộ hóa trước đó. (Các hàng không bao giờ bị xóa). Để biết hàng nào đã được cập nhật hoặc chèn kể từ lần đồng bộ hóa cuối cùng, có một bigintcột RowUpdateCountertrong mỗi bảng.

Ý tưởng là bất cứ khi nào một hàng được chèn hoặc cập nhật, số trong RowUpdateCountercột của nó sẽ thay đổi. Các giá trị đi vào RowUpdateCountercột phải được lấy từ một chuỗi số ngày càng tăng. Các giá trị trong RowUpdateCountercột phải là duy nhất và mỗi giá trị mới được lưu trữ trong một bảng phải lớn hơn bất kỳ giá trị nào trước đó.

Xin vui lòng xem các kịch bản cho thấy các hành vi mong muốn.

Lược đồ

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

XÁC NHẬN một số hàng

INSERT INTO [dbo].[Test]
    ([ID]
    ,[Value]
    ,[RowUpdateCounter])
VALUES
(1, 'A', ???),
(2, 'B', ???),
(3, 'C', ???),
(4, 'D', ???);

Kết quả mong đợi

+----+-------+------------------+
| ID | Value | RowUpdateCounter |
+----+-------+------------------+
|  1 | A     |                1 |
|  2 | B     |                2 |
|  3 | C     |                3 |
|  4 | D     |                4 |
+----+-------+------------------+

Các giá trị được tạo ra RowUpdateCountercó thể khác nhau, giả sử 5, 3, 7, 9. Chúng phải là duy nhất và chúng phải lớn hơn 0, vì chúng tôi đã bắt đầu từ bảng trống.

XÁC NHẬN và CẬP NHẬT một số hàng

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
    ,???)
;

Kết quả mong đợi

+----+-------+------------------+
| ID | Value | RowUpdateCounter |
+----+-------+------------------+
|  1 | A     |                1 |
|  2 | B     |                2 |
|  3 | E     |                5 |
|  4 | F     |                6 |
|  5 | G     |                7 |
|  6 | H     |                8 |
+----+-------+------------------+
  • RowUpdateCounterđối với các hàng có ID 1,2nên giữ nguyên, vì các hàng này không bị thay đổi.
  • RowUpdateCounterđối với các hàng có ID 3,4nên thay đổi, vì chúng đã được cập nhật.
  • RowUpdateCounterđối với các hàng có ID 5,6sẽ thay đổi, vì chúng đã được chèn.
  • RowUpdateCounterđối với tất cả các hàng thay đổi phải lớn hơn 4 (cuối cùng RowUpdateCountertừ chuỗi).

Thứ tự mà các giá trị mới ( 5,6,7,8) được gán cho các hàng thay đổi không thực sự quan trọng. Các giá trị mới có thể có khoảng trống, ví dụ 15,26,47,58, nhưng chúng không bao giờ nên giảm.

Có một số bảng với các quầy như vậy trong cơ sở dữ liệu. Sẽ không có vấn đề gì nếu tất cả chúng sử dụng chuỗi toàn cầu duy nhất cho các số của chúng hoặc mỗi bảng có một chuỗi riêng.


Tôi không muốn sử dụng một cột có tem datetime thay vì bộ đếm số nguyên, bởi vì:

  • Đồng hồ trên máy chủ có thể nhảy cả tiến và lùi. Đặc biệt là khi nó ở trên một máy ảo.

  • Các giá trị được trả về bởi các hàm hệ thống giống SYSDATETIMEnhau cho tất cả các hàng bị ảnh hưởng. Quá trình đồng bộ hóa có thể đọc các thay đổi theo đợt. Ví dụ: nếu kích thước lô là 3 hàng, thì sau MERGEbước trên quy trình đồng bộ sẽ chỉ đọc các hàng E,F,G. Khi quá trình đồng bộ được chạy lần sau, nó sẽ tiếp tục từ hàng H.


Cách tôi đang làm bây giờ khá là xấu xí.

Vì không có SEQUENCEtrong SQL Server 2008, tôi mô phỏng SEQUENCEtheo một bảng chuyên dụng với IDENTITYnhư trong câu trả lời này . Điều này tự nó khá xấu xí và trở nên trầm trọng hơn bởi thực tế là tôi cần phải tạo ra không phải một, mà là một loạt các số cùng một lúc.

Sau đó, tôi có một INSTEAD OF UPDATE, INSERTkích hoạt trên mỗi bảng với RowUpdateCountervà tạo các bộ số cần thiết ở đó.

Trong INSERT, UPDATEMERGEtruy vấn tôi đặt RowUpdateCounter0, mà được thay thế bằng các giá trị đúng trong cò. Các ???truy vấn trên là 0.

Nó hoạt động, nhưng có một giải pháp dễ dàng hơn?


4
Bạn có thể sử dụng phiên bản hàng / dấu thời gian? Đó là trường nhị phân nhưng giá trị sẽ thay đổi mỗi khi hàng được cập nhật
James Z

@JamesZ, tôi cần biết thứ tự các hàng đã được thay đổi. Quá trình đồng bộ hóa đọc Bộ đếm MAX từ bản sao đã lỗi thời của bảng và sau đó nó biết chỉ tìm nạp các hàng có Bộ đếm nhiều hơn giá trị đó. Các rowversionsẽ không cho tôi khả năng này, nếu tôi hiểu đúng nó là gì ... Có đảm bảo được ngày càng tăng?
Vladimir Baranov


Cảm ơn bạn @MartinSmith, tôi hoàn toàn quên mất rowversion. Nó trông rất hấp dẫn. Mối quan tâm duy nhất của tôi là tất cả các ví dụ về việc sử dụng nó mà tôi đã thấy cho đến nay đều xoay quanh việc phát hiện xem một hàng có thay đổi hay không. Tôi cần một cách hiệu quả để biết tập hợp hàng nào đã thay đổi kể từ một thời điểm nhất định. Bên cạnh đó, có thể bỏ lỡ một bản cập nhật?
Vladimir Baranov

@MartinSmith time = 0: giá trị chuyển đổi hàng cuối cùng là, 122. time = 1: Giao dịch Acập nhật một hàng, chuyển đổi hàng của nó thay đổi thành 123, Achưa được cam kết. time = 2: Giao dịch Bcập nhật một hàng khác, chuyển đổi hàng của nó thay đổi thành 124. time = 3: Bcam kết. time = 4: quá trình đồng bộ hóa chạy và tìm nạp tất cả các hàng có chuyển đổi hàng> 122, có nghĩa là các hàng chỉ được cập nhật theo B. thời gian = 5: Acam kết. Kết quả: thay đổi theo Asẽ không bao giờ được chọn bởi quá trình đồng bộ hóa. Tôi có lầm không? Có thể một số sử dụng thông minh MIN_ACTIVE_ROWVERSIONsẽ giúp?
Vladimir Baranov

Câu trả lời:


5

Bạn có thể sử dụng một ROWVERSIONcột cho việc này.

Các tài liệu nói rằng

Mỗi cơ sở dữ liệu có một bộ đếm được tăng cho mỗi thao tác chèn hoặc cập nhật được thực hiện trên một bảng có chứa một cột chuyển đổi trong cơ sở dữ liệu.

Các giá trị là BINARY(8)và bạn nên xem xét chúng BINARYthay BIGINTvì sau khi 0x7FFFFFFFFFFFFFFFnó tiếp tục 0x80...và bắt đầu hoạt động -9223372036854775808nếu được coi là đã ký bigint.

Một ví dụ đầy đủ làm việc dưới đây. Việc duy trì chỉ mục trên ROWVERSIONcột sẽ tốn kém nếu bạn có nhiều cập nhật, do đó bạn có thể muốn kiểm tra khối lượng công việc của mình cả có và không để xem liệu nó có xứng đáng với chi phí không.

CREATE TABLE [dbo].[Test]
  (
     [ID]               [INT] NOT NULL CONSTRAINT [PK_Test] PRIMARY KEY,
     [Value]            [VARCHAR](50) NOT NULL,
     [RowUpdateCounter] [ROWVERSION] NOT NULL UNIQUE NONCLUSTERED
  )

INSERT INTO [dbo].[Test]
            ([ID],
             [Value])
VALUES     (1,'Foo'),
            (2,'Bar'),
            (3,'Baz');

DECLARE @RowVersion_LastSynch ROWVERSION = MIN_ACTIVE_ROWVERSION();

UPDATE [dbo].[Test]
SET    [Value] = 'X'
WHERE  [ID] = 2;

DECLARE @RowVersion_ThisSynch ROWVERSION = MIN_ACTIVE_ROWVERSION();

SELECT *
FROM   [dbo].[Test]
WHERE  [RowUpdateCounter] >= @RowVersion_LastSynch
       AND RowUpdateCounter < @RowVersion_ThisSynch;

/*TODO: Store @RowVersion_ThisSynch somewhere*/

DROP TABLE [dbo].[Test] 

Cảm ơn bạn. Sau khi đọc các tài liệu tôi nghĩ rằng thay vì @@DBTSnên có MIN_ACTIVE_ROWVERSION(), và nếu sử dụng MIN_ACTIVE_ROWVERSION()so sánh <=nên trở thành <>trở thành >=.
Vladimir Baranov

Theo các tài liệu, có một sự khác biệt quan trọng giữa @@DBTSMIN_ACTIVE_ROWVERSION()nếu có các giao dịch không được cam kết hoạt động. Nếu một ứng dụng sử dụng @@DBTSchứ không phải MIN_ACTIVE_ROWVERSION, có thể bỏ lỡ các thay đổi đang hoạt động khi xảy ra đồng bộ hóa.
Vladimir Baranov

@VladimirBaranov - vâng, đồng ý, chỉnh sửa.
Martin Smith

-2

Bạn đã thử sử dụng IDENTITYtùy chọn?

Ví dụ:

[RowUpdateCounter] [bigint] NOT NULL IDENTITY(1,2)

Ở đâu

  • 1 -> Giá trị bắt đầu
  • 2 -> mỗi hàng mới được tăng lên bởi điều này

Điều này tương tự như SEQUENCE trong Oracle.


SQL Server không có bất kỳ "tùy chọn AUTOINCREMENT" nào
Martin Smith

Đúng. Nó được hỗ trợ bởi Access. Máy chủ SQL hỗ trợ tùy chọn IDENTITY. Tôi đã cập nhật trả lời của tôi ở trên. Cảm ơn !!
Bibhuti Bhusan Padhi

4
IDENTITYkhông làm những gì được yêu cầu liên quan đến việc tự động tăng trên cả bản cập nhật và phần chèn .
Martin Smith

@BibhutiBhusanPadhi, tôi cần biết những hàng nào đã được cập nhật. Tôi không thấy cách đơn giản IDENTITYcó thể giúp đỡ.
Vladimir Baranov
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.