我有一些同步过程使用“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语句使用它完成作业的日期而不是它开始的时候它?
答案 0 :(得分:0)
不是将LastUpdate
(或示例代码中的MergeDate
)设置为getdate()
,而是执行以下操作:
declare @MergeDate DateTime = getdate()
<merge code...>
set MergeDate = @MergeDate
<...>
这样,时间戳等于合并开始时的结束时间。然后,您可以多次处理某些行,但这是包含错误而不是排除,并且应该对结果没有影响。
答案 1 :(得分:0)
不是试图强制SQL在合并中使用结束时间(我看不到你这样做),为什么不将每个合并的开始时间存储在一个表中(让我们调用这个dLastRunDate
)
当你开始下一次合并时,不要使用getdate() - 从新表中获取dLastRunDate
并使用它来检查新记录。
然后在作业结束时,将dLastRunDate
更新为新值。
我们在仓库ETLS中使用此方法。每个步骤都在表格中有一个条目。每次作业开始一步时,它都会选择自己的dLastRunDate
并使用它来检查更新的记录。完成此步骤后,它会更新dLastRunDate
及其开始时间。