Я использую 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
, UPDATE
and 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
поможет?Ответы:
Вы можете использовать
ROWVERSION
столбец для этого.В документации говорится, что
Значения есть,
BINARY(8)
и вы должны рассматривать их,BINARY
а неBIGINT
как после0x7FFFFFFFFFFFFFFF
того, как он переходит к0x80...
и начинает работать,-9223372036854775808
если рассматривается как подписанныйbigint
.Полный проработанный пример приведен ниже. Поддержание индекса в
ROWVERSION
столбце будет дорогостоящим, если у вас много обновлений, поэтому вы можете протестировать свою рабочую нагрузку как с использованием, так и без него, чтобы определить, стоит ли это затрат.источник
@@DBTS
этого должно бытьMIN_ACTIVE_ROWVERSION()
, и при использованииMIN_ACTIVE_ROWVERSION()
сравнение<=
должно стать<
и>
стать>=
.@@DBTS
иMIN_ACTIVE_ROWVERSION()
их наличием. Если приложение использует@@DBTS
вместоMIN_ACTIVE_ROWVERSION
, можно пропустить изменения, которые активны, когда происходит синхронизация.Вы пытались использовать
IDENTITY
опцию?Например:
где
Это похоже на ПОСЛЕДОВАТЕЛЬНОСТЬ в Oracle.
источник
IDENTITY
не выполняет то, что требуется для автоматического увеличения как обновлений, так и вставок .IDENTITY
может помочь.