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. Codeforces Round #698 (Div. 2)(A ~ F)6题全,超高质量题解)【每日亿题】2021/2/4
  2. python基础教程_学习笔记14:标准库:一些最爱——re
  3. haproxy keepalived_详解mycat+haproxy+keepalived搭建高可用负载均衡mysql集群
  4. zabbix报错:Zabbix服务启动不了
  5. 《Java程序设计》实验报告——Java的多线程机制
  6. 03_TF2 Guide、文档清单(数据输入、估计器、保存模型、加速器、性能调优等)、TF2库和扩展库(TensorBoard、数据集、TensorFlow Hub、概率和统计分析库、图像处理库)
  7. 《网络编程》ioctl 操作
  8. Android开发启动未注册的activity,Hook使用demo
  9. 【 HDU - 2594 】Simpsons’ Hidden Talents(KMP应用,求最长前缀后缀公共子串)
  10. 大数据-MapReduce计算框架
  11. smart原则_用SMART原则,定位好副业目标
  12. 拓扑排序 java_[Java]聊聊拓扑排序算法
  13. 2020计算机行业就业职位及分析
  14. 天下无贼是假的,天下无票倒是真的;如来神掌是假功夫,能买到车票才是真功夫。
  15. 支持向量机与支持向量回归(support vector machine and support vector regression)
  16. 关于零点和极点的讨论
  17. 单片机节日彩灯实训报告_基于单片机控制的节日彩灯设计.pdf
  18. 快递已经签收怎么查询快递的信息
  19. 服务器端分层架构(丛林战争项目)
  20. Arduino简单实例之五_红外避障传感器模块

热门文章

  1. linux 导出数据库
  2. [PyQt5]基本控件9 - 图片显示QPixmap
  3. NSLog的使用方法
  4. 结构体嵌套枚举给结构体变量赋值注意
  5. 服务器内部服务器错误的原因和解决办法
  6. shiro单点登录原理_shiro集成cas单点登出
  7. 如何用两个队列实现一个栈?
  8. FPGA开发必备软件——Vivado,安装教程
  9. 乐优商城(项目搭建+统一通用异常处理)(一)
  10. MyBatis @SelectKey注解用法介绍