我执行UPDATE
与OUTPUT
查询:
UPDATE BatchReports
SET IsProcessed = 1
OUTPUT inserted.BatchFileXml, inserted.ResponseFileXml, deleted.ProcessedDate
WHERE BatchReports.BatchReportGUID = @someGuid
Run Code Online (Sandbox Code Playgroud)
这句话很好,很好; 直到在表上定义了一个触发器.然后我的UPDATE
陈述将得到错误334:
如果语句包含没有INTO子句的OUTPUT子句,则DML语句的目标表'BatchReports'不能具有任何已启用的触发器
现在,SQL Server团队在博客文章中解释了这个问题:
错误消息是不言自明的
他们还提供解决方案:
该应用程序已更改为使用INTO子句
除了我无法做出整篇博文的正面或反面.
所以让我问一下我的问题:我应该改变什么UPDATE
才能使它有效?
在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 …