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隔离级别即可。

根据多篇参考文章所述,都推荐使用这个方法,该方法性能最佳,原因如下:

  1. Faster performance. The Engine needs to parse, compile, and execute only one query instead of three (and no temporary variable to hold the key).(更快的性能,引擎只需解析、编译和只需一条语句而不是三条,且不需要临时变量保存键值)
  2. Neater and simpler T-SQL code (after you get proficient in MERGE). (干净简洁的代码,当你熟悉merge语法后)
  3. No need for explicit BEGIN TRANSACTION/COMMIT. MERGE is a single statement and is executed in one implicit transaction. (无需显式声明begin transaction/commit. merge是单个声明,在隐式事务中执行)
  4. 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功能相关推荐

  1. sql server 数组_如何在SQL Server中实现类似数组的功能

    sql server 数组 介绍 (Introduction) I was training some Oracle DBAs in T-SQL and they asked me how to cr ...

  2. 如何在SQL Server查询语句(Select)中检索存储过程(Store Procedure)的结果集

    如何在SQL Server查询语句(Select)中检索存储过程(Store Procedure)的结果集?(2006-12-14 09:25:36) 与这个问题具有相同性质的其他描述还包括: 如 ...

  3. 如何在SQL Server数据库中加密数据

    如何在SQL Server数据库中加密数据 为了防止某些别有用心的人从外部访问数据库,盗取数据库中的用户姓名.密码.信用卡号等其他重要信息,在我们创建数据库驱动的解决方案时,我们首先需要考虑的的第一条 ...

  4. SQL Server中的功能与存储过程

    介绍 (Introduction) Usually DBAs prefer stored procedures in SQL instead of functions in SQL Server. I ...

  5. 如何在SQL Server中实现错误处理

    错误处理概述 (Error handling overview) Error handling in SQL Server gives us control over the Transact-SQL ...

  6. 如何在SQL Server Reporting Services中自动创建KPI

    关键绩效指标(KPI) (Key Performance Indicator (KPI)) A Key Performance Indicator aka KPI is a metric which ...

  7. 如果不使用 SQL Mail,如何在 SQL Server 中发送电子邮件

    如果不使用 SQL Mail,如何在 SQL Server 中发送电子邮件 察看本文应用于的产品 文章编号 : 312839 最后修改 : 2006年12月21日 修订 : 10.1 本页 概要 SQ ...

  8. 如何在 SQL Server 2005 故障转移群集中添加或删除节点(安装程序)

    如何在 SQL Server 2005 故障转移群集中添加或删除节点(安装程序) 使用此过程管理 Microsoft SQL Server 2005 故障转移群集实例中的节点. 重要提示: 若要更新或 ...

  9. 无废话-SQL Server 2005新功能(1) - TSQL

    无废话-SQL Server 2005新功能(1) - TSQL SQL Server 2005相对于SQL Server 2000改进很大,有些还是非常实用的. 举几个例子来简单说明 这些例子我引用 ...

  10. SQL Server 2016 新功能之综述

    冬去春来,发现之前最后一篇写在2012年,又过去了5年了,时间如飞啊.那时候SQL 2012 发布让人兴奋了一把,哪知道时间如刀,刀刀催人老啊,今天SQL 2016都发布了很久了,很快SQL On l ...

最新文章

  1. eclipse的怪问题。background indexer crash recovery .java.lang.OutOfMemoryError: Java heap space
  2. ionic3 安装遇到的问题
  3. win7系统升服务器版本,WIN7专业版可update补丁,WIN7旗舰版无法update补丁,WSUS服务器是按windows类型还是版本区别updata的还是其他什么方式...
  4. 电脑屏幕变黄如何调整_如何调整电脑屏幕比例
  5. 一篇文章看懂Java并发和线程安全
  6. hive 元数据 自定义_如何在Hive中创建自定义函数UDF及如何直接通过Impala的同步元数据重用UDF的jar文件-阿里云开发者社区...
  7. 转两篇关于国是的网文
  8. 调整手机titlebar与app的titlebar相衔接
  9. 2.Prometheus 监控技术与实践 --- Prometheus基本概念及部署
  10. 支付宝异步回调验证签名的那些走过的坑
  11. 基于遗传算法的车辆优化调度-matlab代码 考虑供应过剩惩罚、供应不足惩罚成本
  12. 【OpneWRT】编译ipk
  13. js页面刷新事件 ,Javascript刷新页面的几种方法
  14. 虚函数表和虚函数指针
  15. ios label 高度紫石英_iOS_NSMutableAttributedString和自适应宽度高度
  16. 当生命科学遇上AI,会产生怎样1+1>2效果?
  17. 《OKR源于英特尔和谷歌的管理利器》阅读总结
  18. 30行代码统计自己 CSDN 博客相关数据
  19. Admin-UI分布式微服务监控中心
  20. Python + ElasticSearch:有了这个超级武器,你也可以报名参加诗词大会了! | 博文精选...

热门文章

  1. 预测问题评价指标:MAE、MSE、R-Square、MAPE和RMSE
  2. UWB-DW1000的TWR测距及代码(五)
  3. WebStorm 10 及以上版本如何更换主题 (可用于WebStorm 2016.3)
  4. int32_t和int区别
  5. kafka集群搭建及基本使用_CodingPark编程公园
  6. 如何进行数据库的优化?
  7. c++ 中stoi函数用法解析(来自官网)
  8. 【C++标准头文件】<sstream>
  9. 【网络|TCP】三次握手、四次握手
  10. 最全整理,web自动化测试框架总结-实战案例,从0精通封装...