【发布时间】:2011-02-01 02:49:01
【问题描述】:
在 SQL Server 2005 中执行原子“UPSERT”(如果存在则更新,否则插入)的正确模式是什么?
我在 SO 上看到了很多代码(例如,参见 Check if a row exists, otherwise insert),它们具有以下两部分模式:
UPDATE ...
FROM ...
WHERE <condition>
-- race condition risk here
IF @@ROWCOUNT = 0
INSERT ...
或
IF (SELECT COUNT(*) FROM ... WHERE <condition>) = 0
-- race condition risk here
INSERT ...
ELSE
UPDATE ...
其中
我一直在使用以下方法,但我很惊讶在人们的反应中没有看到它,所以我想知道它有什么问题:
INSERT INTO <table>
SELECT <natural keys>, <other stuff...>
FROM <table>
WHERE NOT EXISTS
-- race condition risk here?
( SELECT 1 FROM <table> WHERE <natural keys> )
UPDATE ...
WHERE <natural keys>
请注意,这里提到的竞争条件与前面代码中的竞争条件不同。在较早的代码中,问题是幻读(行被另一个会话插入到 UPDATE/IF 之间或 SELECT/INSERT 之间)。在上面的代码中,竞争条件与 DELETE 有关。在 (WHERE NOT EXISTS) 执行之后但在 INSERT 执行之前,另一个会话是否可以删除匹配的行?目前尚不清楚 WHERE NOT EXISTS 在何处与 UPDATE 一起锁定任何内容。
这是原子的吗?我无法找到这将在 SQL Server 文档中记录的位置。
编辑:我意识到这可以通过事务来完成,但我认为我需要将事务级别设置为 SERIALIZABLE 以避免幻读问题?对于这样一个常见的问题,这肯定是矫枉过正吗?
【问题讨论】:
-
Mladen Prajdić 在这里有一篇您可能会感兴趣的文章。 sqlteam.com/article/… 和这里 weblogs.sqlteam.com/mladenp/archive/2007/07/30/60273.aspx
-
任何请求的正确模式涉及“Atomic”一词和多个SQL语句应该始终与 BEGIN TRANSACTION 和 COMMIT/ROLLBACK 绑定。
标签: sql-server sql-server-2005 atomic upsert