在 SQL Server 2016 上插入或更新的安全解决方案
Safe solutions for INSERT OR UPDATE on SQL Server 2016
假设 table 结构为 MyTable(MyTableId NVARCHAR(MAX) PRIMARY KEY, NumberOfInserts INTEGER)。
我经常需要更新,即增加现有记录的计数器,或者插入一条新记录(如果它不存在且 NumberOfInserts 的值为 0)。
本质上:
IF (MyTableId exists)
run UPDATE command
ELSE
run INSERT command
我担心由于竞争条件等原因导致数据丢失
什么是最安全的写法?
我需要它尽可能 100% 准确,并愿意在必要时牺牲速度。
MERGE
语句可以同时执行 UPDATE
和 INSERT
(如果需要,还可以执行 DELETE
)。
即使它是单个原子语句,使用 HOLDLOCK
查询提示来防止竞争条件也很重要。 Dan Guzman 有一篇博客 post “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
);
当然,您也可以使用显式两步法,单独检查一行是否存在,并分开 UPDATE
和 INSERT
语句。只需确保将它们全部包装在具有适当 table 锁定提示的事务中。
有关详细信息,请参阅 Dan Guzman 的 Conditional INSERT/UPDATE Race Condition。
假设 table 结构为 MyTable(MyTableId NVARCHAR(MAX) PRIMARY KEY, NumberOfInserts INTEGER)。
我经常需要更新,即增加现有记录的计数器,或者插入一条新记录(如果它不存在且 NumberOfInserts 的值为 0)。
本质上:
IF (MyTableId exists)
run UPDATE command
ELSE
run INSERT command
我担心由于竞争条件等原因导致数据丢失
什么是最安全的写法?
我需要它尽可能 100% 准确,并愿意在必要时牺牲速度。
MERGE
语句可以同时执行 UPDATE
和 INSERT
(如果需要,还可以执行 DELETE
)。
即使它是单个原子语句,使用 HOLDLOCK
查询提示来防止竞争条件也很重要。 Dan Guzman 有一篇博客 post “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
);
当然,您也可以使用显式两步法,单独检查一行是否存在,并分开 UPDATE
和 INSERT
语句。只需确保将它们全部包装在具有适当 table 锁定提示的事务中。
有关详细信息,请参阅 Dan Guzman 的 Conditional INSERT/UPDATE Race Condition。