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


25

我有一个表测试,该表具有列ID,主键和自动递增的名称。我只想在没有记录的情况下插入新记录。

输入的ID为30122,名称为john

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

我可以使用2个查询

select * from test where id=30122

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

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

insert into test(name) values('john')

但是我想使用单个查询?

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


1
But I wanted to use single query?为什么?
亚伦·伯特兰

@AaronBertrand我的后端是使用java开发的。因此,如果我使用2条查询,那么我必须命中数据库2次。因此,如果可以使用单个查询完成,那么为什么要使用2条查询
SpringLearner 2015年

1
Java不支持存储过程,或者不支持具有两个语句的批处理,而这些语句只需要对数据库进行一次命中?
亚伦·伯特兰

@AaronBertrand您能举一个如何使用sql server 2008或更高版本处理此问题的示例吗?
eaglei22

1
@ eaglei22我将在下面的vijayp答案中使用第二个示例。我仍然不会选择MERGE任何版本,即使是SQL Server 2019也是如此
亚伦·伯特兰

Answers:


41

你可以试试这个

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

提高性能的其他方法是

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

阅读此不良习惯以使用模式前缀


4
第一个示例很浪费,并且经常会导致死锁-我完全不建议这样做。
亚伦·伯特兰

@AaronBertrand关心详细吗?谢谢
Hexo '17

5
@SlapY当然,在第一个示例中,您说的是:“嘿,SQL Server,是否存在具有此ID的行?” SQL Server可能使用扫描来查找行,然后返回答案。“为什么,是的,用户,我确实有该ID的行!” 然后您说:“好的,SQL Server,再次找到该行,但是这次,更新它!” 您看到两次执行查找或扫描是多么浪费吗?您能想象如果在继续进行某些操作之前,另一个用户向SQL Server询问有关行是否存在的相同问题会发生什么情况?
亚伦·伯特兰

谢谢,我只是不明白为什么第一个威胁会死锁而第二个不是?两者都包含多个语句,如果不以完全锁定运行,则可以将其拦截。我错了吗?
Hexo

2
@ 0x25b3不是一个死锁威胁,另一个不是死锁,而是第一个例子更容易受到死锁的威胁。无论哪种情况,您都应该进行充分而适当的交易,但是人们却没有,所以……
亚伦·伯特兰

17

假设使用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)
);

询问

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);

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

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

神话:并发更新/插入解决方案


8
合并有一些问题
vonPryz 2015年

神话般的链接非常棒。好东西!
JonnyRaa
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.