sql - SQL Server 2016 上插入或更新的安全解决方案

标签 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% 准确,并愿意在必要时牺牲速度。

最佳答案

MERGE语句可以执行UPDATEINSERT(如果需要的话还可以执行DELETE)。

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

查询本身很简单:

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 语句。只需确保将它们全部包装在具有适当表锁定提示的事务中即可。

参见Conditional INSERT/UPDATE Race Condition丹·古兹曼(Dan Guzman)了解详细信息。

关于sql - SQL Server 2016 上插入或更新的安全解决方案,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38219952/

相关文章:

当我尝试编写插入语句时 PHP 语法错误

javascript - 如何通过Javascript从外部网站查询共享点列表

mysql - 'num_of_cmt' 是 4 而不是 2。为什么?

sql - 无法从链接服务器 :--NETSUITE 的 OLE DB 提供程序 "[MSDASQL].LONG_DESCRIPTION"获取列 "MSDASQL"的当前行值

tsql - 在没有游标的情况下删除重复的行和依赖项

python - 使用 SQLAlchemy 批量更新子查询

.net - 使用 Entity Framework 将 SQL Server 数据库导出到 Access

sql-server - 保持简单以及如何在查询中执行多个 CTE

ios - 在绘制之前从数据库中获取数据

sql-server - SQL Server 复制本地订阅属性返回 "Cannot apply value ‘null’ 到属性 ServerInstance : Value cannot be null. "