在SQL Server 2005中执行原子“ UPSERT”(存在时为UPDATE,否则为INSERT)的正确模式是什么?
我在SO上看到了很多代码(例如,请参阅检查是否存在一行,否则请插入),其代码分为以下两部分:
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 ...
其中<condition>是自然键的评估。上述方法似乎都不能很好地处理并发。如果我不能让两行具有相同的自然键,则上述所有情况似乎都有在竞争条件场景中插入具有相同自然键的行的风险。
我一直在使用以下方法,但是我很惊讶没有在人们的回应中看到它,因此我很奇怪它出了什么问题:
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以避免幻像读取问题?对于这样一个普遍的问题,这肯定是大材小用吗?
慕虎7371278
智慧大石
相关分类