我想做一个UPSERT的SELECT / INSERT版本.以下是现有代码的模板:
// CREATE TABLE Table (RowID INT NOT NULL IDENTITY(1,1),RowValue VARCHAR(50)) IF NOT EXISTS (SELECT * FROM Table WHERE RowValue = @VALUE) BEGIN INSERT Table VALUES (@Value) SELECT @id = SCOPEIDENTITY() END ELSE SELECT @id = RowID FROM Table WHERE RowValue = @VALUE)
解决方法
您可以使用LOCKs来实现SERIALIZABLE,但这会降低并发性.为什么不先尝试共同的条件(“大多是插入或大部分选择”),然后安全处理“补救”行动?也就是说,“JFDI”模式…
预计大多数INSERT(球场70-80%):
只是尝试插入.如果失败,行已经创建.不需要担心并发,因为TRY / CATCH处理重复.
BEGIN TRY INSERT Table VALUES (@Value) SELECT @id = SCOPEIDENTITY() END TRY BEGIN CATCH IF ERROR_NUMBER() <> 2627 RAISERROR etc ELSE -- only error was a dupe insert so must already have a row to select SELECT @id = RowID FROM Table WHERE RowValue = @VALUE END CATCH
主要选择:
类似的,但是尝试先获取数据.没有数据=需要INSERT.再次,如果两个并发调用尝试插入,因为它们都发现该行缺少TRY / CATCH句柄.
BEGIN TRY SELECT @id = RowID FROM Table WHERE RowValue = @VALUE IF @@ROWCOUNT = 0 BEGIN INSERT Table VALUES (@Value) SELECT @id = SCOPEIDENTITY() END END TRY BEGIN CATCH IF ERROR_NUMBER() <> 2627 RAISERROR etc ELSE SELECT @id = RowID FROM Table WHERE RowValue = @VALUE END CATCH
第二个似乎重复,但它是高度并发的.锁将实现相同但以并发为代价…
编辑:
为什么不使用MERGE?
如果使用OUTPUT子句,它将只返回更新的内容.所以你需要一个虚拟UPDATE来为OUTPUT子句生成INSERTED表.如果您必须使用许多调用(由OP隐含)进行虚拟更新,那么这是很多日志写入才能够使用MERGE.