在SQL Server 2005中执行原子"UPSERT"(在哪里存在UPDATE,否则为INSERT)的正确模式是什么?
我在SO上看到了很多代码(例如,请参阅检查是否存在行,否则插入),其中包含以下两部分模式:
UPDATE ...
FROM ...
WHERE <condition>
-- race condition risk here
IF @@ROWCOUNT = 0
INSERT ...
Run Code Online (Sandbox Code Playgroud)
要么
IF (SELECT COUNT(*) FROM ... WHERE <condition>) = 0
-- race condition risk here
INSERT ...
ELSE
UPDATE ...
Run Code Online (Sandbox Code Playgroud)
其中<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>
Run Code Online (Sandbox Code Playgroud)
请注意,此处提到的竞争条件与早期代码中的竞争条件不同.在早期的代码中,问题是幻读(在UPDATE/IF之间或在另一个会话的SELECT/INSERT之间插入行).在上面的代码中,竞争条件与DELETE有关.在(WHERE NOT EXISTS)执行之后但在INSERT执行之前,是否有可能由另一个会话删除匹配的行?目前还不清楚WHERE …