如何在SQL Server实现upsert功能
upsert是什么?
相信各位码农在工作当中经常碰到这样的场景,当这个表存在某条记录就更新其值,不存在则插入,如下代码例子所示
IF EXISTS (SELECT 1 FROM mytable WHERE Id = @Id)BEGINUPDATE dbo.mytableSET Value = @ValueWHERE Id = @Id;END
ELSEBEGININSERT INTO dbo.mytable (Id, Value)VALUES(@Id, @Value)END
这就是upsert的概念。你一定会想,这么简单的场景需要讨论吗?如果是在单线程环境下,这个确实没啥好讨论,简单的做下判断即可。但是如果是在多用户并发访问的情况下就不是那么简单了。
并发访问会有什么问题?
会造成重复插入一条相同id的记录,会造成primary key violation, 即违反主键约束,从而造成SQL语句抛出异常。这是由于当并发操作时,可能会存在两个或以上的线程执行时都判断不存在这个id的记录,从而同时执行插入操作。
解决办法
原理:事务 + isolation level serializable + 排他锁(updlock)
SERIALIZABLE隔离级别:
Statements cannot read data that has been modified but not yet committed by other transactions. (语句不能读取其他事务已修改但未提交的数据,即避免脏读)
No other transactions can modify data that has been read by the current transaction until the current transaction completes.(其他事务不能修改当前事务已读取的数据,除非当前事务已完成)
Other transactions cannot insert new rows with key values that would fall in the range of keys read by any statements in the current transaction until the current transaction completes(其他事务不能插入新的记录,该记录的键值属于当前事务读取的键值范围内,除非当前事务已完成).
以下具体方法整理相关参考网址,从个人的理解都是遵循上述原理,只是实现上有些区别。
方法1:
set transaction isolation level serializable
begin transaction
if exists (select 1 from mytable with (updlock) where id = @id)update mytable set value = @value where id = @id;
else insert mytable (id, value) values (@id, @value);
commit
在事务开始前,显式设置隔离级别为serializable ,该隔离级别对当前会话保持有效。
当采用serializable隔离级别后,可以避免出现主键冲突。 但还需要在select语句显式的指定updlock提示,否则容易造成事务之间死锁。因为select语句默认会获取一个RangeS-S共享锁,共享锁是不排他的,即其他事务也会获取这个共享锁。当执行后面的update或insert语句时,当前事务需要将RangeS-S共享锁升级为排他锁,而如果其他事务也拥有这个共享锁,就无法升级,从而导致死锁。加上updlock提示,则一开始select语句获得的就是排他的更新锁,从而避免死锁。(详见https://samsaffron.com/blog/archive/2007/04/04/14.aspx)
方法2
begin tran
if exists (select * from t with (updlock,serializable) where pk = @id)beginupdate mytable set value = @value where id = @id;end
elsebegininsert mytable (id, value) values (@id, @value);end
commit tran
对select语句加提示serializable,是对这个表采用serializable隔离级别,而方法1是隔离级别对当前会话有效。
方法3
先执行update语句,如果执行结果为空,则插入,因update本身默认就是获得更新锁,可以不用加updlock提示。
begin tranupdate mytable with (serializable) set value = @value where id = @id;if @@rowcount = 0begininsert mytable (id, value) values (@id, @value);end
commit tran
根据https://www.cnblogs.com/zhenfengren/p/5618511.html的描述,方法3在都是insert的情况下要好于方法1和2。
方法4
MERGE mytable WITH (serializable) AS tUSING (SELECT @ID AS ID) AS new_idON t.ID = new_id.IDWHEN MATCHED THENUPDATE SET t.value = @valueWHEN NOT MATCHED THENINSERT ( ID, Value ) VALUES (new_id.ID, @value);
从SQL Server 2008之后,微软引入了一个新的的命令语法:Merge,详见 https://docs.microsoft.com/en-us/sql/t-sql/statements/merge-transact-sql?view=sql-server-2017
Merge和insert,update一样,本身是个原子语句声明,不需要用begin tran/commit去显式的声明事务。在这里只需要使用
with (serializable)指定使用serializable隔离级别即可。
根据多篇参考文章所述,都推荐使用这个方法,该方法性能最佳,原因如下:
- Faster performance. The Engine needs to parse, compile, and execute only one query instead of three (and no temporary variable to hold the key).(更快的性能,引擎只需解析、编译和只需一条语句而不是三条,且不需要临时变量保存键值)
- Neater and simpler T-SQL code (after you get proficient in MERGE). (干净简洁的代码,当你熟悉merge语法后)
- No need for explicit BEGIN TRANSACTION/COMMIT. MERGE is a single statement and is executed in one implicit transaction. (无需显式声明begin transaction/commit. merge是单个声明,在隐式事务中执行)
- Greater functionality. MERGE can delete rows that are not matched by source (SRC table above). For example, we can delete row 1 from A_Table because its Data column does not match Search_Col in the SRC table. There is also a way to return inserted/deleted values using the OUTPUT clause.“ (很棒的功能,merge可以删除源未匹配的记录,举例,我们可以删除A表一条记录当数据字段未匹配源表查询,而且还可以通过output子句返回插入或删除的值)
综上所述:个人推荐方法3和方法4,如果是对性能要求比较高的场景,则建议方法4.
参考:
https://www.cnblogs.com/zhenfengren/p/5618511.html
https://docs.microsoft.com/en-us/sql/t-sql/statements/set-transaction-isolation-level-transact-sql?view=sql-server-2017
https://blogs.msdn.microsoft.com/dbrowne/2013/02/25/why-is-tsql-merge-failing-with-a-primary-key-violation-isnt-it-atomic/
https://docs.microsoft.com/en-us/sql/t-sql/statements/merge-transact-sql?view=sql-server-2017
如何在SQL Server实现upsert功能相关推荐
- sql server 数组_如何在SQL Server中实现类似数组的功能
sql server 数组 介绍 (Introduction) I was training some Oracle DBAs in T-SQL and they asked me how to cr ...
- 如何在SQL Server查询语句(Select)中检索存储过程(Store Procedure)的结果集
如何在SQL Server查询语句(Select)中检索存储过程(Store Procedure)的结果集?(2006-12-14 09:25:36) 与这个问题具有相同性质的其他描述还包括: 如 ...
- 如何在SQL Server数据库中加密数据
如何在SQL Server数据库中加密数据 为了防止某些别有用心的人从外部访问数据库,盗取数据库中的用户姓名.密码.信用卡号等其他重要信息,在我们创建数据库驱动的解决方案时,我们首先需要考虑的的第一条 ...
- SQL Server中的功能与存储过程
介绍 (Introduction) Usually DBAs prefer stored procedures in SQL instead of functions in SQL Server. I ...
- 如何在SQL Server中实现错误处理
错误处理概述 (Error handling overview) Error handling in SQL Server gives us control over the Transact-SQL ...
- 如何在SQL Server Reporting Services中自动创建KPI
关键绩效指标(KPI) (Key Performance Indicator (KPI)) A Key Performance Indicator aka KPI is a metric which ...
- 如果不使用 SQL Mail,如何在 SQL Server 中发送电子邮件
如果不使用 SQL Mail,如何在 SQL Server 中发送电子邮件 察看本文应用于的产品 文章编号 : 312839 最后修改 : 2006年12月21日 修订 : 10.1 本页 概要 SQ ...
- 如何在 SQL Server 2005 故障转移群集中添加或删除节点(安装程序)
如何在 SQL Server 2005 故障转移群集中添加或删除节点(安装程序) 使用此过程管理 Microsoft SQL Server 2005 故障转移群集实例中的节点. 重要提示: 若要更新或 ...
- 无废话-SQL Server 2005新功能(1) - TSQL
无废话-SQL Server 2005新功能(1) - TSQL SQL Server 2005相对于SQL Server 2000改进很大,有些还是非常实用的. 举几个例子来简单说明 这些例子我引用 ...
- SQL Server 2016 新功能之综述
冬去春来,发现之前最后一篇写在2012年,又过去了5年了,时间如飞啊.那时候SQL 2012 发布让人兴奋了一把,哪知道时间如刀,刀刀催人老啊,今天SQL 2016都发布了很久了,很快SQL On l ...
最新文章
- eclipse的怪问题。background indexer crash recovery .java.lang.OutOfMemoryError: Java heap space
- ionic3 安装遇到的问题
- win7系统升服务器版本,WIN7专业版可update补丁,WIN7旗舰版无法update补丁,WSUS服务器是按windows类型还是版本区别updata的还是其他什么方式...
- 电脑屏幕变黄如何调整_如何调整电脑屏幕比例
- 一篇文章看懂Java并发和线程安全
- hive 元数据 自定义_如何在Hive中创建自定义函数UDF及如何直接通过Impala的同步元数据重用UDF的jar文件-阿里云开发者社区...
- 转两篇关于国是的网文
- 调整手机titlebar与app的titlebar相衔接
- 2.Prometheus 监控技术与实践 --- Prometheus基本概念及部署
- 支付宝异步回调验证签名的那些走过的坑
- 基于遗传算法的车辆优化调度-matlab代码 考虑供应过剩惩罚、供应不足惩罚成本
- 【OpneWRT】编译ipk
- js页面刷新事件 ,Javascript刷新页面的几种方法
- 虚函数表和虚函数指针
- ios label 高度紫石英_iOS_NSMutableAttributedString和自适应宽度高度
- 当生命科学遇上AI,会产生怎样1+1>2效果?
- 《OKR源于英特尔和谷歌的管理利器》阅读总结
- 30行代码统计自己 CSDN 博客相关数据
- Admin-UI分布式微服务监控中心
- Python + ElasticSearch:有了这个超级武器,你也可以报名参加诗词大会了! | 博文精选...
热门文章
- 预测问题评价指标:MAE、MSE、R-Square、MAPE和RMSE
- UWB-DW1000的TWR测距及代码(五)
- WebStorm 10 及以上版本如何更换主题 (可用于WebStorm 2016.3)
- int32_t和int区别
- kafka集群搭建及基本使用_CodingPark编程公园
- 如何进行数据库的优化?
- c++ 中stoi函数用法解析(来自官网)
- 【C++标准头文件】<sstream>
- 【网络|TCP】三次握手、四次握手
- 最全整理,web自动化测试框架总结-实战案例,从0精通封装...