假设MyTable的表结构(KEY, datafield1, datafield2…)

通常,我希望更新现有记录,或者插入不存在的新记录。

从本质上讲:

IF (key exists)
  run update command
ELSE
  run insert command

最好的写法是什么?


当前回答

IF EXISTS (SELECT * FROM [Table] WHERE ID = rowID)
UPDATE [Table] SET propertyOne = propOne, property2 . . .
ELSE
INSERT INTO [Table] (propOne, propTwo . . .)

编辑:

唉,即使对我自己不利,我也必须承认,没有选择的解决方案似乎更好,因为它们少了一步就完成了任务。

其他回答

这取决于使用模式。一个人必须在不迷失在细节的情况下看到使用的大局观。例如,如果使用模式是在创建记录后99%更新,那么'UPSERT'是最佳解决方案。

在第一次插入(hit)之后,它将全部是单个语句更新,没有if或but。插入的'where'条件是必要的,否则它将插入重复的数据,并且您不希望处理锁定。

UPDATE <tableName> SET <field>=@field WHERE key=@key;

IF @@ROWCOUNT = 0
BEGIN
   INSERT INTO <tableName> (field)
   SELECT @field
   WHERE NOT EXISTS (select * from tableName where key = @key);
END

我尝试了下面的解决方案,它为我工作,当插入语句发生并发请求时。

begin tran
if exists (select * from table with (updlock,serializable) where key = @key)
begin
   update table set ...
   where key = @key
end
else
begin
   insert table (key, ...)
   values (@key, ...)
end
commit tran

MS SQL Server 2008引入了MERGE语句,我认为它是SQL:2003标准的一部分。正如许多人所表明的那样,处理一行情况并不是什么大问题,但当处理大型数据集时,就需要游标,随之而来的是所有性能问题。MERGE语句在处理大型数据集时将非常受欢迎。

不要忘记事务。性能很好,但是简单的方法(IF EXISTS..)非常危险。 当多个线程将尝试执行插入或更新时,您可以轻松地 获取主键冲突。

由@Beau Crawford和@Esteban提供的解决方案显示了大致的想法,但容易出错。

为了避免死锁和PK违规,你可以使用如下方法:

begin tran
if exists (select * from table with (updlock,serializable) where key = @key)
begin
   update table set ...
   where key = @key
end
else
begin
   insert into table (key, ...)
   values (@key, ...)
end
commit tran

or

begin tran
   update table with (serializable) set ...
   where key = @key

   if @@rowcount = 0
   begin
      insert into table (key, ...) values (@key,..)
   end
commit tran
IF EXISTS (SELECT * FROM [Table] WHERE ID = rowID)
UPDATE [Table] SET propertyOne = propOne, property2 . . .
ELSE
INSERT INTO [Table] (propOne, propTwo . . .)

编辑:

唉,即使对我自己不利,我也必须承认,没有选择的解决方案似乎更好,因为它们少了一步就完成了任务。