2016-05-13 4 views
0

У меня есть некоторые процессы синхронизации, которые используют флаг «LastUpdate» для обновления любых записей, которые были изменены с момента последней попытки синхронизации.SQL Merge, Параметры значения таблицы и GetDate()

Некоторое время назад я обновил код, чтобы использовать параметры таблицы, а не синхронизировать (добавлять/обновлять) строку за раз. Это в 10 раз или быстрее.

Однако я столкнулся с состоянием гонки, которое иногда вызывает пропуски обновлений. Я быстро шумели некоторые SQL скрипт, чтобы проверить мою ситуацию/теории (любая большая таблица с идентификаторами будет работать):

/*CREATE TYPE IntTable AS TABLE(
[RequestID] [int] NOT NULL 
) 
GO 

CREATE TABLE MergeTest(
[ID] [int] IDENTITY(1,1) NOT NULL, 
[RequestID] [int] NOT NULL, 
[PreDate] [datetime] NOT NULL, 
[MergeDate] [datetime] NOT NULL 
GO 
*/ 

DECLARE @requestIDs As IntTable 

INSERT INTO @requestIDs 
SELECT RequestID FROM Request 

DECLARE @preDate As DateTime = Getdate() 

MERGE INTO MergeTest USING @requestIDs SRC 
ON MergeTest.RequestID = SRC.RequestID 
WHEN MATCHED THEN 
    UPDATE SET PreDate = @preDate, MergeDate = GetDate() 
WHEN NOT MATCHED THEN 
    INSERT (RequestID, PreDate, MergeDate) 
    VALUES (SRC.RequestID, @preDate, GetDate()); 

SELECT TOP 100 * FROM MergeTest 

Пример Результат

ID RequestID PreDate     MergeDate 
1 169880  2016-05-13 13:57:54.643 2016-05-13 13:57:54.643 

Таким образом, вы можете увидеть, что MergeDate (GetDate ()) происходит от момента начала слияния, а не после его окончания.

Состояние гонки может быть это:

Check what has been updated since 14:59 
Start a merge at 15:00 
Check what has been updated since 15:00 
Merge completes, but with a LastUpdate of 15:00 
Check what has been updated since 15:01 

Все записи из слияния будут пропущены. На самом деле это состояние гонки очень редко происходит, потому что мы говорим миллисекунды, а не минуты, но это происходит.

Вопрос: без запуска второго скрипта для повторного обновления LastUpdate с датой после слияния, есть ли способ заставить оператор слияния использовать дату, когда он завершил работу, а не когда начал?

ответ

0

Вместо установки LastUpdate (или MergeDate, в примере кода), чтобы getdate(), сделать что-то вроде этого:

declare @MergeDate DateTime = getdate() 

<merge code...> 
set MergeDate = @MergeDate 
<...> 

Таким образом, метка времени равно, когда началось слияние, не закончилась. Затем вы можете обрабатывать несколько строк более одного раза, но это ошибка включения, а не исключение, и не должна влиять на результаты.

0

Вместо того, чтобы пытаться заставить SQL использовать время окончания в слиянии (что я не вижу, что вы делаете), почему бы не сохранить время начала каждого слияния в таблице (позвольте называть это dLastRunDate).

Когда вы начинаете следующее слияние, не используйте getdate() - возьмите dLastRunDate из новой таблицы и используйте это для проверки новых записей.

Затем в конце задания обновите dLastRunDate до нового значения.

Мы используем этот подход в нашем складе ETLS. Каждый шаг имеет запись в таблице. Каждый раз, когда задание начинается с шага, он берет свой собственный dLastRunDate и использует его для проверки обновленных записей. Когда шаг сделан, он обновляет dLastRunDate с его началом.

Смежные вопросы