说明:
mysql执行inset语句并未明确说明限制多少条语句,但是sql语句在超过一定的大小后,执行就会报错,百度得知mysql 5.7 最大语句执行最大不能超过4M,当然,在mysql执行批量插入时,如果批量插入的语句过长则将会报错,所以我们需要限制一下批量插入的数量,分批进行插入,分享两个场景
1. 场景一
当我们在代码中经过一系列的逻辑处理后,得到了一个未知长度的list,需要将这个未知长度的list插入到数据库中(未知长度的list可能会超过1w条数据,或者超过10w条数据,再或者更长,我们无法预支得到的长度并且长度)
java 核心代码:
/** 经过各种逻辑处理获取到的未知长度list*/List<RSUserInfo> userInfos = rSUserInfoService.selectRSUserInfoList(rSUserInfo);/** 批量加入数据库 核心代码*/int batchNum = 5000; // 设置批量导入条数,可根据业务背景进行变动int start, end;/** 计算出需要执行批量导入的次数*/int size = userInfos.size() % batchNum == 0 ? userInfos.size() / batchNum : userInfos.size() / batchNum + 1;for (int i = 0; i < size; i++) {start = i * batchNum;end = Math.min(i * batchNum + batchNum - 1, userInfos.size() - 1);/** 创建一个新的list*/List<RSUserInfo> newList = new ArrayList<>();for (int k = start; k <= end; k++) {/** 遍历未userInfos插入newList*/newList.add(userInfos.get(k));}/** 执行批量导入sql 参数 newList*/this.rSUserInfoService.insertUserInfos(newList);}return AjaxResult.success("批量插入数据成功!");
mapper层:
int insertUserInfos(List<RSUserInfo> newList);
mapper.xml
<insert id="insertUserInfos" parameterType="list">insert into r_s_user_info(user_name, user_sex, user_age, phone_no, wechat_no,user_type, birthday, remarks,create_time, update_time)values<foreach collection="list" item="item" separator=",">(#{item.userName},#{item.userSex},#{item.userAge},#{item.phoneNo},#{item.wechatNo},#{item.userType},#{item.birthday},#{item.remarks},#{item.createTime},#{item.updateTime})</foreach></insert>
思路分析
限制下每次批量导入的条数,计算出需要执行批量导入的次数,在分次进行批量导入,限制条数是为了不让sql语句大小超过mysql的最大限制,防止sql语句执行报错
1. 场景二
当我们需要将A表中的数据进行处理后和过滤后,插入到B表中,在A表中获取到的数据在处理过程中条数不会变多,如果存在过滤处理后,数据条数只会变得更少,可以进行分页查询A表中的数据,处理后直接批量插入到B表,限制查询的数据条数,从而限制sql语句的大小
如果在A表中的数据处理后可能会产生比之前更多的数据(无法预估的长度),这样情况就符合场景一中的使用,即直接将场景一中的代码引入使用
不建议的用法
将多条语句导入到数据库中,也可以在java的for循环中,每次循环一条数据,然后执行一次数据库插入操作,这样方法对于极少数据没什么影响,如果数据量超过了上万条,执行时间无线变长,而且对于数据库的资源消耗是很大的,不建议这样使用
总结:
代码中需要使用到批量导入数据库的场景时,限制导入的条数是对数据库比较友好的交互,即使可以预测到批量导入的条数,其实加上每次批量限制对于代码的执行效率影响是可以忽略不记的,在保证代码不会报错的前提下优化代码执行效率