SQL Server 2016上INSERT或UPDATE的安全解决方案

时间:2016-07-06 08:56:35

标签: sql sql-server sql-server-2016

假设MyTable的表结构(MyTableId NVARCHAR(MAX)PRIMARY KEY,NumberOfInserts INTEGER)。

我经常需要更新,即递增现有记录的计数器,或者如果NumberOfInserts的值不为0,则插入新记录。

本质:

IF (MyTableId exists)
    run UPDATE command
ELSE
    run INSERT command

我担心的是由于竞争条件等导致的数据丢失。

最安全的方法是什么?

如果可能的话,我需要100%准确,并且愿意在必要时牺牲速度。

1 个答案:

答案 0 :(得分:8)

MERGE语句可以执行UPDATEINSERT(以及DELETE,如果需要)。

即使它是单个原子语句,使用HOLDLOCK查询提示来防止竞争条件也很重要。 Dan Guzman撰写了一篇博文“UPSERT” Race Condition With MERGE,详细解释了它的工作原理,并提供了一个测试脚本来验证它。

查询本身很简单:

DECLARE @NewKey NVARCHAR(MAX) = ...;

MERGE INTO dbo.MyTable WITH (HOLDLOCK) AS Dst
USING 
(
    SELECT @NewKey AS NewKey
) AS Src
ON Src.NewKey = Dst.[Key]
WHEN MATCHED THEN
UPDATE
SET NumberOfInserts = NumberOfInserts + 1
WHEN NOT MATCHED THEN
INSERT
(
    [Key]
    ,NumberOfInserts
)
VALUES
(
    @NewKey
    ,0
);

当然,您还可以使用显式两步方法,单独检查是否存在行,并分隔UPDATEINSERT语句。只需确保使用适当的表锁定提示将它们全部包装在一个事务中。

有关详细信息,请参阅Dan Guzman的Conditional INSERT/UPDATE Race Condition