2016-05-13 60 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及其开始时间。