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

Kni*_*ins 5 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
Run Code Online (Sandbox Code Playgroud)

我担心的是由于竞争条件等原因而丢失数据.

什么是最安全的写这种方式?

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

Vla*_*nov 8

MERGE语句既可以执行UPDATE,也可以执行INSERT(DELETE如果需要).

即使它是单个原子语句,使用HOLDLOCK查询提示来防止竞争条件也很重要.Dan Guzman 有一篇博客文章"UPSERT"与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
);
Run Code Online (Sandbox Code Playgroud)

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

有关详细信息,请参阅Dan Guzman的条件INSERT/UPDATE竞争条件.

  • 小心使用MERGE ...它有很多问题:https://www.mssqltips.com/sqlservertip/3074/use-caution-with-sql-servers-merge-statement/ (2认同)