MySQL新增插入,重复更新操作业务实现
新增插入,重复则更新或不插入操作
- 1. 业务代码控制(先查后插)
- 2. SQL 层面控制
- 使用`EXISTS`判断
- 使用索引
在写业务的时候,时常会遇到这样的情况,需要对数据进行处理,若是新的数据就直接新增到数据库中,若是数据库中已经存在的数据就不进行处理或者进行更新操作。
我在实现的过程中想到了几种方式,记录下来。
1. 业务代码控制(先查后插)
在 Java 代码中循环遍历数据,先查询是否存在,再决定是否插入(并发场景需加锁,否则可能仍有重复):
/*** 批量插入:先查后插,避免重复* @param billList 待插入数据列表*/
public void batchInsertWithCheck(List<BillDTO> billList) {for (BillDTO item : billList) {// 1. 查询是否存在重复记录int count = projectSubjectAmountMapper.countByUniqueKeys(item.getSubProjectId(), item.getRateCode(), item.getSubjectCode());// 2. 不存在则插入if (count == 0) {projectSubjectAmountMapper.insertSingle(item);}}
}
若是执行更新操作的话,则对查询到的重复记录执行更新操作即可。
缺点
- 性能差:每条数据都需执行一次查询,数据量大时数据库交互频繁;
- 并发风险:多线程场景下,“查询 - 插入” 的时间差可能导致重复插入(需额外加分布式锁或使用事务)。
2. SQL 层面控制
使用EXISTS
判断
<insert id="batchInsertIfNotExist">INSERT INTO bus_project_subject_amount(project_id,tax_code,subject_code,amount)-- 用UNION ALL拼接多条待插入数据,每条数据先判断是否存在<foreach collection="billList" item="item" separator="UNION ALL">SELECT #{item.subProjectId}, #{item.rateCode}, #{item.subjectCode}, #{item.amount}FROM DUAL -- DUAL是MySQL虚拟表,无实际数据,仅用于构造查询WHERE NOT EXISTS (-- 子查询:判断当前记录是否已存在SELECT 1 FROM bus_project_subject_amountWHERE project_id = #{item.subProjectId}AND tax_code = #{item.rateCode}AND subject_code = #{item.subjectCode})</foreach>
</insert>
使用索引
情况1:若是想要对已经存在的数据进行更新操作,批量插入数据,若某条记录已存在(唯一键重复),则将新数据的amount累加到原有记录的amount中(如:原有金额 100,新数据金额 50,更新后为 150)。
<insert id="batchInsertOrUpdateAmount">INSERT INTO bus_project_subject_amount(project_id,tax_code,subject_code,amount) VALUES<foreach collection="billList" item="item" separator=",">(#{item.subProjectId}, #{item.rateCode}, #{item.subjectCode}, #{item.amount})</foreach>-- 唯一键冲突时,执行金额累加更新ON DUPLICATE KEY UPDATEamount = amount + VALUES(amount)
</insert>
- 需要在数据库中建立联合唯一索引,在唯一索引冲突的情况下,就会执行我想要的更新操作。
ON DUPLICATE KEY UPDATE
:MySQL 特有语法,当插入触发唯一键冲突时,不报错而是执行后续更新逻辑;VALUES(amount)
:指代本次插入语句中amount字段的新值,amount = amount + VALUES(amount)
即 “原金额 + 新金额”。- 在本次实践中由于我业务的特殊情况,会有重复数据的情况,因此我添加了一列计算列作为唯一索引。(计算列即我使用的几个字段的连接)
情况2:若是对已经存在的数据不进行处理,批量插入数据,若某条记录已存在(唯一键重复),则忽略该条数据的插入操作(不报错、不更新,直接跳过),仅插入无冲突的新数据。
<insert id="batchInsertIgnoreDuplicate">-- 关键:用INSERT IGNORE替代普通INSERTINSERT IGNORE INTO bus_project_subject_amount(project_id,tax_code,subject_code,amount) VALUES<foreach collection="billList" item="item" separator=",">(#{item.subProjectId}, #{item.rateCode}, #{item.subjectCode}, #{item.amount})</foreach>
</insert>
ON DUPLICATE KEY UPDATE
:MySQL 特有语法,当插入触发唯一键冲突时,不报错而是执行后续更新逻辑;VALUES(amount)
:指代本次插入语句中amount
字段的新值,amount = amount + VALUES(amount)
即 “原金额 + 新金额”。
The End.