如果在SQL Server 2008中存在INSERT,则更新UPDATE

UPDATE if exists else INSERT in SQL Server 2008

本问题已经有最佳答案,请猛点这里访问。

我想知道如何使用一个语句在SQL Server中使用UPSERT或换句话说UPDATE if records exists Else enter new record操作?

此示例显示了在Oracle Here中实现此目的的方法
但它使用Dual表,而SQL Server中不存在。

那么,任何SQL Server替代品(没有存储过程)好吗?


很多人会建议你使用MERGE,但我提醒你不要这样做。默认情况下,它不会保护您不受多个语句的并发和竞争条件的影响,但它确实会引入其他危险:

http://www.mssqltips.com/sqlservertip/3074/use-caution-with-sql-servers-merge-statement/

即使使用这种"更简单"的语法,我仍然更喜欢这种方法(为简洁省略了错误处理):

1
2
3
4
5
6
7
8
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
UPDATE dbo.table SET ... WHERE PK = @PK;
IF @@ROWCOUNT = 0
BEGIN
  INSERT dbo.table(PK, ...) SELECT @PK, ...;
END
COMMIT TRANSACTION;

很多人会这样建议:

1
2
3
4
5
6
7
8
9
10
11
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
IF EXISTS (SELECT 1 FROM dbo.table WHERE PK = @PK)
BEGIN
  UPDATE ...
END
ELSE
BEGIN
  INSERT ...
END
COMMIT TRANSACTION;

但所有这一切都可以确保您可能需要两次读取表以找到要更新的行。在第一个示例中,您只需要找到一次行。 (在这两种情况下,如果从初始读取中找不到行,则会发生插入。)

其他人会建议这样:

1
2
3
4
5
6
7
BEGIN TRY
  INSERT ...
END TRY
BEGIN CATCH
  IF ERROR_NUMBER() = 2627
    UPDATE ...
END CATCH

但是,如果除了在几乎每个插入失败的罕见情况下,除了让SQL Server捕获您可能首先阻止的异常之外没有其他原因,这是有问题的。我在这里证明了这一点:

  • http://www.mssqltips.com/sqlservertip/2632/checking-for-potential-constraint-violations-before-entering-sql-server-try-and-catch-logic/
  • http://www.sqlperformance.com/2012/08/t-sql-queries/error-handling

通过单一陈述不确定您认为您获得了什么;我认为你没有任何收获。 MERGE是单个语句,但它仍然必须真正执行多个操作 - 即使它让你认为它没有。