使用循环将单个CSV导入MySQL多表时遇命令不同步错误求助
解决mysqli_multi_query导致的"Commands out of sync; you can't run this command now"错误
嘿,我之前也踩过这个坑,咱们来一步步搞定它。这个错误的核心原因是:当你用mysqli_multi_query执行多条语句后,没有处理完所有返回的结果集,MySQL连接会一直处于"等待处理结果"的状态,导致下一轮循环的查询无法执行。
先看你的代码问题
你在循环里每次都用mysqli_multi_query执行事务、插入、SELECT 433503这些语句,其中SELECT 433503会返回一个结果集。如果你不读取并释放这个结果集,MySQL就会认为上一次的查询还没完成,自然不让你执行下一次查询,就抛出了那个错误。
方案1:修复mysqli_multi_query的用法
如果你想继续用multi_query,那每次执行完后,必须把所有结果集都处理掉。在你的mysqli_multi_query后面加上这段代码:
mysqli_multi_query($db,$sql) or die("Error: ".mysqli_error($db)); // 关键:循环处理所有结果集,直到没有剩余 do { // 如果有结果集,就释放它 if ($result = mysqli_store_result($db)) { mysqli_free_result($result); } // 切换到下一个结果集,直到返回false } while (mysqli_next_result($db));
这段代码会把multi_query执行后产生的所有结果(包括SELECT 433503的结果)都读取并释放,让连接回到可用状态,下一轮循环就能正常执行了。
方案2:更推荐的写法——拆分语句执行
其实mysqli_multi_query并不太适合这种场景,不仅容易出问题,代码也不好维护。更稳妥的方式是把每个语句分开执行,用mysqli_insert_id()来获取最后插入的ID,同时用事务API来管理提交和回滚:
$handle = fopen($file_tmp, 'r'); // 开启事务 mysqli_begin_transaction($db); try { while (($myData = fgetcsv($handle,1000,';')) !== FALSE) { // 保留你原来的变量赋值 $p1 = mysqli_real_escape_string($db,$myData[0]); $p2 = mysqli_real_escape_string($db,$myData[1]); // ... 省略其他p3到p22的赋值 ... $p22 = mysqli_real_escape_string($db,$myData[21]); // 1. 插入kualitas_produk $sql = "INSERT INTO `kualitas_produk`(`responden`,`p1`,`p2`,`p3`,`p4`,`p5`,`p6`,`p7`,`p8`,`p9`) VALUES ('','$p1','$p2','$p3','$p4','$p5','$p6','$p7','$p8','$p9');"; mysqli_query($db, $sql) or throw new Exception(mysqli_error($db)); // 获取最后插入的ID(替代SELECT 433503) $lastId = mysqli_insert_id($db); // 2. 插入harga_produk $sql = "INSERT INTO `harga_produk`(`responden`,`p10`,`p11`,`p12`) VALUES ('$lastId','$p10','$p11','$p12');"; mysqli_query($db, $sql) or throw new Exception(mysqli_error($db)); // 3. 插入keputusan_pembelian $sql = "INSERT INTO `keputusan_pembelian`(`responden`,`p13`,`p14`,`p15`,`p16`,`p17`,`p18`,`p19`,`p20`,`p21`,`p22`) VALUES ('$lastId', '$p13','$p14','$p15', '$p16','$p17','$p18', '$p19','$p20','$p21', '$p22');"; mysqli_query($db, $sql) or throw new Exception(mysqli_error($db)); // 4. 插入recap $sql = "INSERT INTO `recap`(`responden`,`kualitas`,`harga`,`keputusan`) VALUES ('$lastId','$lastId','$lastId','$lastId');"; mysqli_query($db, $sql) or throw new Exception(mysqli_error($db)); } // 所有数据插入成功,提交事务 mysqli_commit($db); echo "Data Added"; } catch (Exception $e) { // 出错就回滚事务,避免部分数据插入 mysqli_rollback($db); die("Error: " . $e->getMessage()); } fclose($handle); mysqli_close($db);
这个写法的好处:
- 用
mysqli_begin_transaction、mysqli_commit、mysqli_rollback来管理事务,比手动写START TRANSACTION和COMMIT更可靠; - 用
mysqli_insert_id()直接获取最后插入的ID,不需要处理额外的结果集; - 出错时能及时回滚,保证数据的一致性;
- 代码更清晰,调试起来也方便。
额外的优化建议
- 如果
kualitas_produk表的responden是自增主键,那插入的时候可以省略这个字段,直接写:
INSERT INTO `kualitas_produk`(`p1`,`p2`,`p3`,`p4`,`p5`,`p6`,`p7`,`p8`,`p9`) VALUES ('$p1','$p2','$p3','$p4','$p5','$p6','$p7','$p8','$p9');
这样MySQL会自动生成自增ID,mysqli_insert_id()也能正确获取。
- 尽量使用预处理语句替代
mysqli_real_escape_string,能更好地防止SQL注入,代码也更简洁。比如插入kualitas_produk可以改成:
$stmt = mysqli_prepare($db, "INSERT INTO `kualitas_produk`(`p1`,`p2`,`p3`,`p4`,`p5`,`p6`,`p7`,`p8`,`p9`) VALUES (?,?,?,?,?,?,?,?,?)"); // 绑定参数,'sssssssss'表示9个字符串类型的参数 mysqli_stmt_bind_param($stmt, 'sssssssss', $p1, $p2, $p3, $p4, $p5, $p6, $p7, $p8, $p9); mysqli_stmt_execute($stmt); $lastId = mysqli_stmt_insert_id($stmt); mysqli_stmt_close($stmt);
这样就不用手动转义每个变量了,安全又高效。
内容的提问来源于stack exchange,提问作者Widhi
相关产品推荐
相关产品推荐

