如何在PHP中使用多重while循环插入数据?多表场景求助
解决PHP多表关联插入的多重循环问题
嘿,我明白你在多表插入时遇到的头疼问题了——涉及5个表的关联操作,稍不注意就会出现数据不一致或者循环逻辑混乱的情况。先帮你理清楚核心逻辑:你应该是想把临时表so_detail_temp里的服务条目,关联servis_detil拿到物料信息,然后生成主订单so,再把明细写入so_detail,同时同步到outbox对吧?
下面给你一套清晰的解决方案,重点解决循环嵌套和数据一致性的问题:
核心注意事项
- 事务必须开启:多表插入是原子操作,要么全部成功,要么全部回滚,避免出现部分数据插入的尴尬情况
- 用预处理语句:循环里直接拼SQL会有注入风险,而且性能差,预处理能解决这两个问题
- 先主表后子表:必须先插入
so表拿到soNo,才能给so_detail和关联表用
完整示例代码
<?php $con = mysqli_connect('localhost', 'root', '', 'a.karat'); // 检查连接 if (!$con) { die("数据库连接失败: " . mysqli_connect_error()); } // 只在点击保存按钮时执行逻辑 if(isset($_POST['save'])){ // 开启事务 mysqli_begin_transaction($con); try { // -------------------------- // 1. 插入主表so,获取soNo // -------------------------- // 假设soNo是自增字段,或者你可以自定义生成规则 $customer = mysqli_real_escape_string($con, $_POST['Customer']); $date_masuk = mysqli_real_escape_string($con, $_POST['Date_masuk']); $date_keluar = mysqli_real_escape_string($con, $_POST['Date_keluar']); $note = mysqli_real_escape_string($con, $_POST['note']); $status = 'pending'; // 假设默认状态 $sql_so = "INSERT INTO so (Customer, Date_masuk, Date_keluar, note, status) VALUES ('$customer', '$date_masuk', '$date_keluar', '$note', '$status')"; mysqli_query($con, $sql_so); $soNo = mysqli_insert_id($con); // 获取刚插入的soNo // -------------------------- // 2. 预处理子表插入语句(提升性能+防注入) // -------------------------- // 预处理so_detail插入 $stmt_detail = mysqli_prepare($con, "INSERT INTO so_detail (soNo, kode_servis, name_servis, qty) VALUES (?, ?, ?, ?)"); mysqli_stmt_bind_param($stmt_detail, "issi", $soNo, $kode_servis, $name_servis, $qty); // 预处理outbox插入 $stmt_outbox = mysqli_prepare($con, "INSERT INTO outbox (number, date, kode_brg, nama_brg, harga, jumlah) VALUES (?, NOW(), ?, ?, ?, ?)"); mysqli_stmt_bind_param($stmt_outbox, "issidi", $soNo, $kode_brg, $nama_brg, $harga, $jumlah); // -------------------------- // 3. 循环读取临时表数据,关联servis_detil并插入 // -------------------------- $sql_temp = "SELECT * FROM so_detail_temp"; $result_temp = mysqli_query($con, $sql_temp); while($row_temp = mysqli_fetch_assoc($result_temp)){ $kode_servis = $row_temp['kode_servis']; $name_servis = $row_temp['nama_servis']; $qty = $row_temp['qty']; // 插入so_detail mysqli_stmt_execute($stmt_detail); // 从servis_detil获取对应的物料信息 $sql_servis = "SELECT code_brg, name_brg, qty FROM servis_detil WHERE code_servis = ?"; $stmt_servis = mysqli_prepare($con, $sql_servis); mysqli_stmt_bind_param($stmt_servis, "s", $kode_servis); mysqli_stmt_execute($stmt_servis); $result_servis = mysqli_stmt_get_result($stmt_servis); while($row_servis = mysqli_fetch_assoc($result_servis)){ $kode_brg = $row_servis['code_brg']; $nama_brg = $row_servis['name_brg']; $jumlah = $row_servis['qty'] * $qty; // 服务对应的物料数量乘以服务数量 $harga = 0; // 这里你需要根据实际情况获取物料价格,比如从物料表读取 // 插入outbox mysqli_stmt_execute($stmt_outbox); } mysqli_stmt_close($stmt_servis); } // -------------------------- // 4. 清空临时表(如果需要的话) // -------------------------- mysqli_query($con, "TRUNCATE TABLE so_detail_temp"); // 提交事务 mysqli_commit($con); echo "数据插入成功!"; header("Location: soAdd.php"); exit(); } catch (Exception $e) { // 出错回滚事务 mysqli_rollback($con); echo "插入失败: " . $e->getMessage(); } // 关闭预处理语句和连接 mysqli_stmt_close($stmt_detail); mysqli_stmt_close($stmt_outbox); mysqli_close($con); } ?>
关键细节说明
- 事务处理:用
mysqli_begin_transaction开启,成功就commit,失败就rollback,保证数据一致性 - 预处理语句:把循环里的SQL提前编译,避免重复解析,同时防止SQL注入
- 关联逻辑:从临时表拿到服务编码后,关联
servis_detil获取对应的物料,再计算总数量插入outbox - 错误捕获:用try-catch捕获异常,方便排查问题
内容的提问来源于stack exchange,提问作者user9688834
相关产品推荐
相关产品推荐

