【问题标题】:Safe solutions for INSERT OR UPDATE on SQL Server 2016SQL Server 2016 上的 INSERT OR UPDATE 安全解决方案
【发布时间】:2016-07-06 08:56:34
【问题描述】:

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

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

基本上:

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

我担心由于竞争条件等而丢失数据。

写这个最安全的方法是什么?

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

【问题讨论】:

  • 您可以考虑使用 MERGE 命令,它同时结合了更新、插入和删除。有几个观察,请不要将该列称为“键”,它是 SQL Server 中的保留字。另外,您是否将其用作主键?如果是这样,NVARCHAR(MAX) 是一个非常糟糕的主意。

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


【解决方案1】:

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

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2020-05-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-08
  • 1970-01-01
  • 2023-02-20
  • 1970-01-01
相关资源
最近更新 更多