如果有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 />
#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
评论
但是我想使用单个查询?为什么?@AaronBertrand我的后端是使用java开发的,所以如果我使用2个查询,那么我必须命中数据库2次,所以如果可以使用单个查询完成,那么为什么要使用2个查询
Java不支持存储过程,或者不支持带有两个语句的批处理,而这两个语句只需要对数据库进行一次命中?
@ eaglei22我将在下面的vijayp答案中使用第二个示例。我仍然不会选择任何版本的MERGE,即使是SQL Server 2019也是如此。这里有一些背景知识。
FWIW,相当于MySQL / MariaDB在DUPLICATE KEY UPDATE上。