You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 08:20:33