当前位置:首页 > 问答 > 正文

插入数据主键重复了咋整,数据库里insert技巧分享和解决办法

当我们往数据库里添加新数据的时候,最常碰到也最让人头疼的问题之一,就是系统突然弹出一个错误,告诉你主键重复了,这就像你去办理身份证,工作人员一查系统,发现已经有一个和你一模一样的身份证号码存在了,那肯定办不了,数据库里的主键就是这个“身份证号码”,它必须是唯一的,不能重复。

为什么会发生这种情况呢?原因其实挺多的,根据CSDN博客上一位开发者的总结,最常见的原因就是你插入的数据里,那个作为主键的字段的值,在数据库的表格里已经存在了,你有一张用户表,主键是用户ID,你试图插入一个用户ID为100的新用户,但表里早就有一个ID是100的老用户了,这一下就撞车了,另一种情况是,可能程序在生成这个主键值的时候逻辑出了错,比如本该生成101,结果又生成了一个100,还有一种可能是,数据库本身设置了主键自动增长,但你插入数据时偏偏又手动指定了一个值,这个值可能就和自动增长出来的值冲突了。

知道了原因,我们来看看怎么解决,硬来肯定是不行的,你不能把已经存在的数据给覆盖了,那会出大乱子,我们需要一些更聪明的办法。

第一个办法,可以叫做“先看看再动手”,也就是说,在真正执行插入命令之前,先让数据库帮我们查一下,这个主键值是不是已经有人用了,具体的做法就是写两条SQL语句,第一条是查询,第二条是插入,并且把它们放在一个逻辑里,如果查询发现没有重复,再执行插入;如果已经有了,那我们就得想别的办法,比如给用户报个错,或者换个主键值,这种方法很直接,但有个小缺点,就是需要连着和数据库交互两次,在高并发的场景下,可能你刚查完发现没有,另一个程序瞬间插入了这个值,你还是会失败,不过在很多要求不那么极端的情况下,这招够用了。

第二个办法,是直接利用数据库提供的“绝招”,也就是类似INSERT IGNORE或者ON DUPLICATE KEY UPDATE这样的SQL语句,这是更推荐的做法,因为它更高效,而且能一步到位。

INSERT IGNORE它的态度是“忽略”,你让我插入数据,我就去插,如果发现主键重复了,我也不报错,我就当没事发生,默默地跳过这条插入指令,这在一些允许“有就最好,没有就算了”的场景下很好用,比如记录用户的某些非关键性操作日志,重复了也无所谓。

但更多的时候,我们遇到主键重复,并不是想简单地忽略它,而是希望“更新”它,用户修改了他的个人信息,提交上来的还是那个用户ID,但姓名、电话可能变了,这时候,ON DUPLICATE KEY UPDATE就派上大用场了,这个语句的意思就是“如果插入时发现主键重复了,那么不要报错,转而执行更新操作,把这条新数据里其他的字段值,更新到老数据上去”,这简直太方便了,一句SQL就同时解决了插入和更新两种需求,省去了我们前面说的“先查询判断”的步骤,很多程序员在知乎上讨论时都认为,这是处理这类问题最优雅和高效的方式之一。

第三个思路,是从根源上解决问题,也就是确保主键永远不会重复,对于数值型的主键,最省心的办法就是交给数据库自己去管理,也就是设置“自增主键”,你插入数据时根本不用管主键字段,数据库会自动给你分配一个比当前最大值大1的数字,这样理论上就不会重复了,自增主键也有自己的局限,比如在分布式系统中可能会需要更复杂的方案。

除了自增,还有一种方法是使用一些天然的、具备唯一性的信息来做主键,比如用户的身份证号、产品的唯一编码等,但用这些需要非常小心,因为一旦这些业务规则发生变化,会非常麻烦,除非有十足把握,否则还是用和业务无关的代理主键(比如自增ID)更稳妥。

还有一种高级技巧,就是专门设计一种生成唯一ID的算法,比如雪花算法,这种算法能在分布式环境下,生成大体有序且全局唯一的ID,从根本上杜绝了主键冲突的可能,不过这属于更深入的优化了,一般在项目初期如果数据量不大,用自增主键就足够了。

碰到主键重复别慌张,它就是一个很常见的技术问题,你可以根据实际情况,选择“先查后插”的稳妥策略,或者直接用INSERT IGNOREON DUPLICATE KEY UPDATE这样的数据库特性来高效处理,如果可能,从一开始就设计好主键生成策略,比如使用自增主键,能帮你省去很多后期的麻烦,希望这些来自日常开发中的技巧分享,能对你下次遇到同样问题时有所帮助。

插入数据主键重复了咋整,数据库里insert技巧分享和解决办法