文章目录

  • 摘要
  • 方案一:加锁
  • 方案二:Unique和Replace Into … SELECT …
  • 方案三: 通过预插入语句判断是否存在记录
  • 方案四: 通过INSERT ... ON DUPLICATE KEY UPDATE


摘要

某些场景会有这样的需求:无记录则插入,有记录则更新。例如:新增用户,以身份证号码作为唯一身份标识,插入时若先查询是否存在记录再决定插入还是更新,在高并发情况下必然存在问题。本文提供三种解决方案。

方案一:加锁

无论通过synchronized锁、ReentranLock锁还是分布式锁,都可以解决该问题。缺点是,加锁会影响性能。方法二和三都是数据库层面解决方案,个人感觉比方法一好一些。

方案二:Unique和Replace Into … SELECT …

首先对唯一性的字段添加唯一索引ALTER TABLE tb_name ADD UNIQUE (col1、col2...),,通过唯一索引即可保证数据的唯一性。

加入唯一索引后,通过INSERT INTO插入相同数据就会报错,此时需要使用REPLACE INTO插入数据,用法是一样的。通过REPLACE INTO插入数据时,若存在相同数据,会将之前的记录删除,再重新插入数据。缺点是,存在先删除再插入的过程,sql需要考虑全部数据列,不然会丢失部分列的数据。缺点是,建立唯一索引会影响插入效率。下面是具体的例子。

# 建立索引
ALTER TABLE user ADD UNIQUE (id_card);
# 假设user表只有id,name,id_card三个字段,且id字段自增。
# 现在需要插入name=ly,id_card=142733的记录。
# 但是,若之前存在id_card=142733的记录,修改name=ly即可。
REPLACE INTO user (id,name,id_card) 
SELECT id,'ly',142733 FROM user RIGHT JOIN (SELECT 1) AS tab 
ON user.id_card = 142733;

通过RIGHT JOIN (SELECT 1),若存在id_card=142733的记录,执行sql后会将原始id保存在临时的结果集中,随name和id_card一同插入。若不存在该记录,则将null作为id随name和id_card一同插入。最终实现

方案三: 通过预插入语句判断是否存在记录

通过预插入语句,尝试插入,判断修改的记录是否大于0,若大于0表示插入成功,若为0则表示记录已存在,需要执行更新操作。

# 预插入
INSERT INTO user (name,id_card)
SELECT 'ly',142733 FROM DUAL 
WHERE NOT EXISTE (SELECT id_card FROM user WHERE id_card = 142733) ;
# 若预插入语句插入成功(修改记录数=1),则无需后续操作。否则执行更新操作。
UPDATE user SET name = 'ly' WHERE id_card = 142733;

通过NOT EXISTE条件,若存在id_card=142733的记录则伪表DUAL记录为空,预插入语句修改记录为0,此时需要执行更新操作。若不存在id_card=142733的记录,则伪表DUAL记录为一行且内容是'ly',142733,预插入语句修改记录为1,此时不必执行更新语句。

但是发现了有的时候,当我们集成到项目中时,该方法不生效,下面是我自己的方法,项目是SpringBoot+MyBatis的,亲测有效。欢迎尝试。

方案四: 通过INSERT … ON DUPLICATE KEY UPDATE

该方法需要根据我们表中的一列数据(必须是唯一的,可以设置唯一索引UNIQUE)也是一种通过预插入来判断表中是否已存在该记录,存在就更新,不存在就插入。

先查看我们表中的数据

mysql存在则更新不存在则新增 mysql有则更新无则新增_mysql存在则更新不存在则新增


这里,我将表worker_health_code的id_card字段设置了唯一索引(不会设置唯一索引的百度或者也可以只用navicat设计表点击索引即可)。

预插入语句:

insert worker_health_code (worker_name,id_card,gender,address) 
	values('张三','340406196305171018', 1, '江苏省-苏州市') 
	ON DUPLICATE KEY UPDATE worker_name=values(worker_name),id_card=values(id_card),gender=values(gender),address=values(address);

mysql存在则更新不存在则新增 mysql有则更新无则新增_mysql存在则更新不存在则新增_02


再次查看表中的数据,发现并没有新增一列数据,数据发生了变化

mysql存在则更新不存在则新增 mysql有则更新无则新增_加锁_03


不明白的可以自己动手试试。这里我们是预插入了一条id_card已存在的数据,我们只是更新了表中的数据,并没有新增一条数据。我们如果预插入一条id_card不存在的数据呢,会不会新增一条数据呢?答案是肯定的,不信你们大可以动手试一试。

每次分享我遇到过的知识点。让我们一起共同进步。