2016-05-13 57 views
0

我有一些同步過程使用「LastUpdate」標誌來更新自上次同步嘗試以來更改的任何記錄。SQL合併,表值參數和GetDate()

稍後我更新了代碼以利用表值參數,而不是一次同步(添加/更新)一行。這是更快的10倍或更多。

但是,我現在遇到了一個競賽條件,有時會導致更新被錯過。我趕緊張羅了一些SQL腳本來測試我的情況/理論(任何大表ID將工作):

/*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及其開始時間。