大型翻车现场:如何实现记录存在的话就更新,如果记录不存在的话就插入。很简单、但是很实用

导读

Hi,大家好!我是白日梦!本文是MySQL专题的第 26 篇。

下文还是白日梦以自导自演的方式,围绕“如何实现记录存在的话就更新,如果记录不存在的话就插入。”展开本话题。看看你能抗到第几问吧

换一种写作风格,自导自演面试现场!感觉这样还是比较有趣的,欢迎大家订阅我的MySQL专题,公众号首发!持续更新中~

点击阅读原文,格式会好看一点哦~

点击阅读原文,格式会好看一点哦~

点击阅读原文,格式会好看一点哦~

欢迎关注白日梦,公众号首发!持续连载中

1
好!我们开始吧! 
                              

Hi同学,前面表现的不错哦。关于MySQL的基础掌握的还可以,现在有时间继续吗?


嗯,有时间!



白日梦有话说是啊,那能挂吗? 这个专题还有好多文章没写完呢!挂了怎么往下写?


那我们继续面吧。出一道场景题:现在我的业务中有这样的需求:如果目标记录存在的话我就更新它,如果记录不存在的话我就插入。说说看你知道哪些实现方式吧!


嗯,比如我可以像下面这样做


这种方式。

// 伪代码user=User.FindById(1)if user == null{  user.Insert()}else{  user.Update()}



嗯,这确实可以。但是你有更好的方式吗?看你代码,每次都是先查询、再更新/写入。至少都会和MySQL发生两次网络交互哦!

嗯,还可以像下面这样,我先尝试更新,如果没有这条记录的话,更新函数返回的影响行数就是0。于是我根据这个影响行数去判断,当影响行数为0时说明数据库中没有这条记录。于是我就写入。



通过这种方式和MySQL之间的网络请求次数就有可能降低成1。

// 伪代码effectRows=User.UpdateById(1)if effectRows >0 {  user.Insert()}



我看未必吧!并且你这代码存在安全隐患啊!比如遇到这种情况:user实例中的信息和数据库中的记录完全一致。然后你拿着user中的信息去更新数据库中的信息。实际上就没有发生任何改变。也就是说,你的代码中的UpdateById(1)的返回值是0!



然后你的代码进入else中,很显然id=1的行已经存在了,你还执行insert xxx id = 1,这肯定会报错说:主键冲突啊!


我靠!大佬说的对啊!按你这么说,这确实是个风险。即使每次去更新的时候携带上最新的时间戳也无法保证它一定不会进入到else中!




嗯,对的。我们继续这个话题,你还知道其他的实现方式吗?我提示你一下:你有没有使用过 insert ignore into 语句?


嗯嗯,使用过,insert ignore into的作用是:如果记录存在了就忽略本次ignore本次插入。如果记录不存在就写入。



关于insert ignore into的实战可以看这个示例


Step1:创建库表


Step2:正常写入一条数据


Step3:使用insert ignore 重复写入和上一条SQL完全一种的数据


Step4:使用insert ignore写入一条新的数据,会发现可以写入成功。


Step5:使用insert ignore写入,测试一下如果想写入的数据的id(唯一key)已经在表中存在了,其他的非唯一键数据不一样。你会发现:也不能重复写入




嗯,通过你的实验可以看出:insert ignore into的功能是:如果数据已经存在了就忽略本次写入,如果数据不存在就insert。通过上面你做的实验也可以看出它判断是否可以写入的标准是:唯一键不能重复。


只要你想写入的数据和现有的唯一键冲突了,最终就不会将你的数据落库。


嗯嗯,这么看来insert ignore into 并不能满足我们的业务需求。不过我还了解 replace into




嗯!那你说说这个replace 吧!


,replace into的作用是:如果数据已经存在了我就更新。如果数据不存在就更写入。


而判断数据是否已经存在的标准依然是:判断唯一键是否重复。




嗯,继续。


嗯嗯,可以看下面的这个例子:


Step1:如果数据不存在replace 可以将数据写入进去


Step2:如果数据存在replace 可以使用新数据替换旧数据。


而且这个替换还是全量替换:




嗯,很好,使用replace into 确实能做到一次网络请求就实现我们的业务需求。



其实我还知道另一种实现方式:也可以通过一次网络请求实现咱们的业务需求。




哦?你说说看!


使用 on duplicate key update也可以实现,如果记录存在就更新,如果记录不存在就插入。



关于: on duplicate key update 可以看这个例子:


Step1: 如果已经存在了,就更新。


Step2:如果不存在就写入

(上面两图中的ignore不影响)




白日梦补充:下图截自MySQL官网,有一些值得我们注意的点:


其实你想一下,通常情况下,我们业务代码中的insert语句是没有指定主键的id等于多少的。因为这个主键会自己增加。官网中有描述:如果你每次使用on duplicate key update进行更新时(注意是更新而不是插入),MySQL也会让last_insert_id变大。这就会出现id不连续增长的现象。

你可以看这个示例,我复现了一下主键id不连续增长的现象。


Step1: 创建数据表


Step2使用 on duplicate key update 写入一条记录。此时LAST_INSERT_ID为1


Step3: 重复执行,使用 on duplicate key update 将shanghai更新成beijing。此时LAST_INSERT_ID同样会+1,变成2。


Step4: 验收结果的时刻来了。使用 on duplicate key update 插入name = hunan的行,注意观察行的主键id为3。




小伙子可以的!整体感觉还不错。


不久会给你安排下一面


我没有问题了,你有什么想问我的吗?


感谢大佬,欢迎关注我,点赞、在读、打赏、转发马上安排上!



推荐阅读

  1. MySQL的修仙之路,图文谈谈如何学MySQL、如何进阶!(已发布)
  2. 面前突击!33道数据库高频面试题,你值得拥有!(已发布)
  3. 大家常说的基数是什么?(已发布)
  4. 讲讲什么是慢查!如何监控?如何排查?(已发布)
  5. 对NotNull字段插入Null值有啥现象?(已发布)
  6. 能谈谈 date、datetime、time、timestamp、year的区别吗?(已发布)
  7. 了解数据库的查询缓存和BufferPool吗?谈谈看!(已发布)
  8. 你知道数据库缓冲池中的LRU-List吗?(已发布)
  9. 谈谈数据库缓冲池中的Free-List?(已发布)
  10. 谈谈数据库缓冲池中的Flush-List?(已发布)
  11. 了解脏页刷回磁盘的时机吗?(已发布)
  12. 用十一张图讲清楚,当你CRUD时BufferPool中发生了什么!以及BufferPool的优化!(已发布)
  13. 听说过表空间没?什么是表空间?什么是数据表?(已发布)
  14. 谈谈MySQL的:数据区、数据段、数据页、数据页究竟长什么样?了解数据页分裂吗?谈谈看!(已发布)
  15. 谈谈MySQL的行记录是什么?长啥样?(已发布)
  16. 了解MySQL的行溢出机制吗?(已发布)
  17. 说说fsync这个系统调用吧! (已发布)
  18. 简述undo log、truncate、以及undo log如何帮你回滚事物! (已发布)
  19. 我劝!这位年轻人不讲MVCC,耗子尾汁! (已发布)
  20. MySQL的崩溃恢复到底是怎么回事? (已发布)
  21. MySQL的binlog有啥用?谁写的?在哪里?怎么配置 (已发布)
  22. MySQL的bin log的写入机制 (已发布)
  23. 删库后!除了跑路还能干什么?(已发布)
  24. 自导自演的面试现场,趣学数据库的10种文件(已发布)
  25. 大型面试现场:一条update sql执行都经历什么?(已发布)
  26. 大型翻车现场:如何实现记录存在的话就更新,如果记录不存在的话就插入。(已发布)

最后,欢迎关注白日梦的公号哦~

换一种写作风格,自导自演面试现场!感觉这样还是比较有趣的,欢迎大家订阅我的MySQL专题,公众号首发!持续更新中~

点击阅读原文,格式会好看一点哦~

点击阅读原文,格式会好看一点哦~

点击阅读原文,格式会好看一点哦~

posted @ 2021-02-05 13:08  赐我白日梦  阅读(4526)  评论(22编辑  收藏  举报