Я использую SQL Server 2008 Standard, который не имеет SEQUENCEфункции.
Внешняя система считывает данные из нескольких выделенных таблиц основной базы данных. Внешняя система хранит копию данных и периодически проверяет изменения данных и обновляет их копию.
Чтобы сделать синхронизацию эффективной, я хочу передать только те строки, которые были обновлены или вставлены после предыдущей синхронизации. (Строки никогда не удаляются). Чтобы узнать, какие строки были обновлены или вставлены с момента последней синхронизации, в каждой таблице есть 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, так как мы начали с пустой таблицы.
Вставить и обновить некоторые строки
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должен оставаться как есть, потому что эти строки не были изменены.RowUpdateCounterдля строк с идентификатором3,4должен измениться, потому что они были обновлены.RowUpdateCounterдля строк с идентификатором5,6должен измениться, потому что они были вставлены.RowUpdateCounterдля всех измененных строк должно быть больше 4 (последняяRowUpdateCounterиз последовательности).
Порядок, в котором новые значения ( 5,6,7,8) назначаются измененным строкам, на самом деле не имеет значения. Новые значения могут иметь пробелы, например 15,26,47,58, но они никогда не должны уменьшаться.
В базе есть несколько таблиц с такими счетчиками. Не имеет значения, используют ли все они одну глобальную последовательность для своих номеров, или у каждой таблицы есть своя индивидуальная последовательность.
Я не хочу использовать столбец с отметкой даты и времени вместо целочисленного счетчика, потому что:
Часы на сервере могут прыгать как вперед, так и назад. Особенно, когда это на виртуальной машине.
Значения, возвращаемые системными функциями наподобие
SYSDATETIMEодинаковы для всех затронутых строк. Процесс синхронизации должен иметь возможность считывать изменения в пакетах. Например, если размер пакета составляет 3 строки, то после выполненияMERGEшага выше процесс синхронизации будет читать только строкиE,F,G. Когда процесс синхронизации будет запущен в следующий раз, он продолжится со строкиH.
То, как я это делаю сейчас, довольно некрасиво.
Поскольку SEQUENCEв SQL Server 2008 его нет, я эмулирую его SEQUENCEс помощью специальной таблицы, IDENTITYкак показано в этом ответе . Это само по себе довольно уродливо и усугубляется тем фактом, что мне нужно генерировать не одну, а группу чисел за раз.
Затем у меня есть INSTEAD OF UPDATE, INSERTтриггер на каждой таблице с RowUpdateCounterи генерировать необходимые наборы чисел там.
В запросах INSERT, UPDATEand MERGEя устанавливаю RowUpdateCounterзначение 0, которое заменяется правильными значениями в триггере. В ???запросах выше 0.
Это работает, но есть ли более простое решение?
rowversionне дало бы мне такой возможности, если бы я правильно понял, что это такое.
rowversion. Это выглядит очень заманчиво. Единственное, что меня беспокоит, - это то, что все примеры его использования, которые я видел до сих пор, вращаются вокруг определения, изменилась ли одна строка. Мне нужен эффективный способ узнать, какой набор строк изменился с определенного момента. Кроме того, возможно ли пропустить обновление?
Aобновляет строку, ее версия строки изменяется на 123, Aеще не зафиксировано. time = 2: транзакция Bобновляет другую строку, ее версия строки изменяется на 124. time = 3: Bфиксирует. time = 4: процесс синхронизации запускается и извлекает все строки со значением rowversion> 122, что означает, что строки обновляются только с помощью B. время = 5: Aфиксирует Результат: изменения Aникогда не будут обнаружены процессом синхронизации. Я ошибаюсь? Может быть, какое-то умное использование MIN_ACTIVE_ROWVERSIONпоможет?