THINKPHP大批量新增数据 # 需求 使用thinkphp5.1进行excel导入时,需要处理大量的新增数据,并且在事务中需要新增后获取自增ID用户字表的外键。 # 解决方案 一开始使用tp自带的saveAll()方法,打印出sql后发现。 如插入100条数据,则执行了100次insert into table () value (); 而不是使用insert into table () values (),()... PS:查看源码后发现saveAll方法是for循环调取create()方法。 插入100条数据耗时10秒。 好处是使用saveAll方法或返回每一条的数据集(带主键) > **saveAll方法新增数据返回的是包含新增模型(带自增ID)的数据集对象。** 效率太低了,何况假设父表有100条数据,字表有500条数据,执行时间居然高达5分钟。 查看文档发现使用Db类的insertAll方法是拼装insert into table () values (),()... 并且为了防止sql拼接过长,还可使用limit分批插入 ~~~ // 分批写入 每次最多100条数据 Db::name('user')->data($data)->limit(100)->insertAll(); ~~~ 但是返回值为插入的条数,需要锁表获取刚才被插入的数据的ID。 ~~~ Db::startTrans(); try { $j = 0; $subdata = []; $data = []; foreach ($detailData as $key => $value) { //组装父表的数据 // ... $data[$key] = $value; foreach ($week_data_array as $subkey => $subval) { //组装子表的数据 // ... $subdata[$j] = $subval; //此处应该插入父表的ID,但是由于父表ID缺失,先将父表的SKU(SKU是唯一的)塞入子表,以便插入后能查到父表ID $subdata[$j]['sku'] = $value['sku']; $j++; } } $result = Db::name('主表')->data($data)->limit(50)->insertAll(); $result = Db::name('主表')->field('id,sku')->order('id desc')->lock(true)->limit(count($data))->select(); foreach ($result as $key => $value) { foreach ($subdata as $subkey => $subvalue) { if (isset($subvalue['sku']) && $value['sku'] == $subvalue['sku']) { $subdata[$subkey]['product_id'] = $value['id']; unset($subdata[$subkey]['sku']); } } } //防止sql拼接过长,每批次插入50个 Db::name('子表')->data($subdata)->limit(50)->insertAll(); Db::commit(); return ajaxReturn(0, 'success', ['id' => $model->id]); } catch (\Exception $e) { Db::rollback(); return ajaxReturn(-1, '系统繁忙' . $e->getMessage()); } ~~~ 目前执行100条父表,500条子表 父子表均使用saveAll方法,时间为4分52秒 父表使用saveAll,子表使用insertAll,时间为16秒 父子表使用insertAll,插入时间为2.43秒 父子表均使用insertAll,获取自增ID上好像不是很严谨(mysql getLastInsID返回的是第一条插入成功的ID,如分批次插入,则获取最后一批次的第一条ID,故无法获取到正确的自增ID),不知道这么使用是否严谨。