如何使用单个查询插入或更新?

Spr*_*ner 38 sql-server merge upsert

我有一个表测试,其中包含主键和自动递增的列 id 和名称。当且仅当没有记录时,我想插入一条新记录。例如

输入是 id=30122 和 name =john

如果有 ID 为 30122 的记录,那么我将 name 列更新为 john,如果没有记录,那么我将插入一条新记录。

我可以使用 2 个查询,例如

select * from test where id=30122
Run Code Online (Sandbox Code Playgroud)

如果它有一些记录,那么我可以使用 update test set name='john' where id=3012

或者如果它没有记录,那么我可以使用

insert into test(name) values('john')
Run Code Online (Sandbox Code Playgroud)

但我想使用单个查询?

有人可以告诉它是否可能吗?

vij*_*ayp 53

你可以试试这个

IF EXISTS(select * from test where id=30122)
   update test set name='john' where id=3012
ELSE
   insert into test(name) values('john');
Run Code Online (Sandbox Code Playgroud)

获得更好性能的其他方法是

update test set name='john' where id=3012
IF @@ROWCOUNT=0
   insert into test(name) values('john');
Run Code Online (Sandbox Code Playgroud)

阅读这个坏习惯来启动模式前缀

  • @SlapY 当然,在第一个示例中,您是在说:“嘿,SQL Server,有此 ID 的行吗?” SQL Server 开始查找该行,可能使用扫描,然后返回答案。“为什么,是的,用户,我确实有一排有那个 ID!” 然后你说,“好吧,SQL Server,再去找那行 **,但这次,更新它!” 你看到执行两次查找或扫描是多么浪费吗?你能想象如果另一个用户在你开始做某事之前向 SQL Server 询问关于一行是否存在的相同问题会发生什么吗? (9认同)
  • 第一个例子很浪费,经常会导致死锁——我根本不建议这样做。 (5认同)
  • 谢谢,我只是不明白为什么第一个可能会陷入僵局,而第二个则不会?两者都包含多个语句,如果不以完全锁定运行,则可以拦截这些语句。我错了吗? (3认同)
  • @ 0x25b3 并不是说​​一个受到死锁的威胁,而另一个则没有,而是第一个例子更容易受到死锁的威胁。在任何一种情况下,您都应该进行完整且正确的交易,但人们没有,所以...... (3认同)
  • @AaronBertrand 愿意详细说明吗?谢谢 (2认同)

Eva*_*kas 19

假设 SQL Server 2008 或更高版本,您可以使用MERGE

桌子

CREATE TABLE dbo.Test
(
    id integer NOT NULL,
    name varchar(30) NULL,

    CONSTRAINT PK_dbo_Test__id
        PRIMARY KEY CLUSTERED (id)
);
Run Code Online (Sandbox Code Playgroud)

询问

MERGE dbo.Test WITH (SERIALIZABLE) AS T
USING (VALUES (3012, 'john')) AS U (id, name)
    ON U.id = T.id
WHEN MATCHED THEN 
    UPDATE SET T.name = U.name
WHEN NOT MATCHED THEN
    INSERT (id, name) 
    VALUES (U.id, U.name);
Run Code Online (Sandbox Code Playgroud)

SERIALIZABLE提示需要在高并发正确操作

您可以在此处找到 Michael J. Swart 对常用方法的比较:

消除误解:并发更新/插入解决方案

  • 合并有 [一些问题](http://www.mssqltips.com/sqlservertip/3074/use-caution-with-sql-servers-merge-statement/)。 (8认同)