如何使用INSERT WHERE NOT EXISTS正确实现非重复数据插入
问题根因
原SQL出现批量重复插入的核心原因是待插入结果集的行数不符合预期:
- 语句中
select 4, 4 from test的逻辑是遍历test表现有数据逐行返回常量值4、4,初始test表有3条记录,这部分查询会直接生成3行(4,4)的待插入数据 where not exists判断的是SQL语句执行开始时的表快照状态,执行前表内确实没有val1=4、val2=4的记录,因此3行待插入数据全部满足条件,最终被批量插入
*注:子查询里的limit 1没有实际作用,判断存在性时数据库会自动在匹配到第一条记录后终止扫描。
修正SQL
直接用单值行构造代替从原表查询,保证待插入结果集只有1行即可。MySQL环境可以直接写不带from子句的常量查询,也可以用各数据库通用的dual虚拟表:
insert into test (val1, val2) select 4, 4 from dual where not exists ( select 1 from test where val1 = 4 and val2 = 4 );
如果需要一次性批量插入多组需要判重的值,可以用行构造语法定义待插入数据集:
insert into test (val1, val2) select tmp.* from ( -- 括号内列出所有需要插入的数值组 values row(4,4), row(5,5), row(6,6) ) tmp(val1, val2) where not exists ( select 1 from test old where old.val1 = tmp.val1 and old.val2 = tmp.val2 );
注意事项
这种写法在默认事务隔离级别下,无法完全规避并发场景的重复插入问题:如果两个会话同时执行插入同一条数据的语句,双方在执行not exists判断时都读不到对方未提交的插入数据,最终还是会出现重复。如果存在高并发插入同值的场景,需要结合业务场景加事务锁、调整隔离级别来兜底,适配无法加唯一约束做数据库层强校验的业务限制。
内容的提问来源于stack exchange,提问作者user15348043
相关产品推荐
相关产品推荐

