我有一个表测试,该表测试的列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')


但是我想使用单个查询? br />

评论

但是我想使用单个查询?为什么?

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

Java不支持存储过程,或者不支持带有两个语句的批处理,而这两个语句只需要对数据库进行一次命中?

@ eaglei22我将在下面的vijayp答案中使用第二个示例。我仍然不会选择任何版本的MERGE,即使是SQL Server 2019也是如此。这里有一些背景知识。

FWIW,相当于MySQL / MariaDB在DUPLICATE KEY UPDATE上。

#1 楼

您可以尝试使用此方法

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


,并阅读此不良习惯插入模式前缀

评论


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

–亚伦·伯特兰(Aaron Bertrand)
15年1月20日在14:48

@AaronBertrand关心详细吗?谢谢

– Hexo
17年8月21日在12:44

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

–亚伦·伯特兰(Aaron Bertrand)
17年8月21日在13:25

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

– Hexo
17年8月21日在13:30

@ 0x25b3不是一个受到死锁的威胁,另一个不是死锁,这是第一个例子更容易发生死锁。无论哪种情况,您都应该进行完整而适当的交易,但是人们却没有,所以...

–亚伦·伯特兰(Aaron Bertrand)
19年5月10日在18:55

#2 楼

假设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提示是高并发下正确操作所必需的。神话:并发更新/插入解决方案

评论


合并有一些问题。

– vonPryz
2015年1月20日14:00

神话般的链接非常棒。好一个!

–JonnyRaa
19年11月20日在16:15