SQL合并,表值参数和GetDate()

时间:2016-05-13 14:36:48

标签: sql sql-server merge table-valued-parameters

我有一些同步过程使用“LastUpdate”标志来更新自上次同步尝试以来已更改的所有记录。

不久前,我更新了代码以利用表值参数,而不是一次同步(添加/更新)一行。这快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,有没有办法让merge语句使用它完成作业的日期而不是它开始的时候它?

2 个答案:

答案 0 :(得分:0)

不是将LastUpdate(或示例代码中的MergeDate)设置为getdate(),而是执行以下操作:

declare @MergeDate DateTime = getdate()

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

这样,时间戳等于合并开始时的结束时间。然后,您可以多次处理某些行,但这是包含错误而不是排除,并且应该对结果没有影响。

答案 1 :(得分:0)

不是试图强制SQL在合并中使用结束时间(我看不到你这样做),为什么不将每个合并的开始时间存储在一个表中(让我们调用这个dLastRunDate

当你开始下一次合并时,不要使用getdate() - 从新表中获取dLastRunDate并使用它来检查新记录。

然后在作业结束时,将dLastRunDate更新为新值。

我们在仓库ETLS中使用此方法。每个步骤都在表格中有一个条目。每次作业开始一步时,它都会选择自己的dLastRunDate并使用它来检查更新的记录。完成此步骤后,它会更新dLastRunDate及其开始时间。