CSV映射导入数据至双表的PDO SQL查询构建问题
双表批量插入解决方案
要实现同时向products和product_details表插入数据,核心是保证两张表的记录能正确关联(通常通过products的主键,比如product_id),同时必须修复原有代码的SQL注入风险,并通过事务确保数据一致性。以下是两种可行方案:
方案一:逐行插入(适合小数据量)
逐行插入products后,立刻获取自增主键product_id,再插入product_details,用事务包裹确保原子性。
if(isset($_POST["sku"])) { session_start(); $connect = new PDO("mysql:host=localhost; dbname=feed_import", "feed_import", "tSpIn35M"); // 开启事务,保证数据一致性 $connect->beginTransaction(); try { $file_data = $_SESSION['file_data']; unset($_SESSION['file_data']); // 预处理products插入语句(参数绑定避免SQL注入) $productStmt = $connect->prepare(" INSERT INTO products (sku, category_id, price, discount_rate, vat_rate, stock, external_link) VALUES (:sku, :category_id, :price, :discount_rate, :vat_rate, :stock, :external_link) "); // 预处理product_details插入语句 $detailStmt = $connect->prepare(" INSERT INTO product_details (product_id, title, description) VALUES (:product_id, :title, :description) "); foreach($file_data as $row) { // 绑定products参数 $productStmt->bindValue(':sku', $row[$_POST["sku"]]); $productStmt->bindValue(':category_id', $row[$_POST["category_id"]]); $productStmt->bindValue(':price', $row[$_POST["price"]]); $productStmt->bindValue(':discount_rate', $row[$_POST["discount_rate"]]); $productStmt->bindValue(':vat_rate', $row[$_POST["vat_rate"]]); $productStmt->bindValue(':stock', $row[$_POST["stock"]]); $productStmt->bindValue(':external_link', $row[$_POST["external_link"]]); $productStmt->execute(); // 获取刚插入的product_id(自增主键) $productId = $connect->lastInsertId(); // 绑定product_details参数并插入 $detailStmt->bindValue(':product_id', $productId); $detailStmt->bindValue(':title', $row[$_POST["title"]]); $detailStmt->bindValue(':description', $row[$_POST["description"]]); $detailStmt->execute(); } // 提交事务 $connect->commit(); echo 'Data Imported Successfully'; } catch(PDOException $e) { // 出错则回滚所有操作 $connect->rollBack(); echo 'Import Failed: ' . $e->getMessage(); } }
方案二:批量插入+关联查询(适合大数据量)
先批量插入products,再通过唯一字段sku关联获取product_id,批量插入product_details,效率更高。
if(isset($_POST["sku"])) { session_start(); $connect = new PDO("mysql:host=localhost; dbname=feed_import", "feed_import", "tSpIn35M"); $connect->beginTransaction(); try { $file_data = $_SESSION['file_data']; unset($_SESSION['file_data']); // 准备批量插入products的参数 $productValues = []; $productParams = []; foreach($file_data as $row) { $productValues[] = "(?, ?, ?, ?, ?, ?, ?)"; $productParams[] = $row[$_POST["sku"]]; $productParams[] = $row[$_POST["category_id"]]; $productParams[] = $row[$_POST["price"]]; $productParams[] = $row[$_POST["discount_rate"]]; $productParams[] = $row[$_POST["vat_rate"]]; $productParams[] = $row[$_POST["stock"]]; $productParams[] = $row[$_POST["external_link"]]; } // 执行products批量插入 $productQuery = " INSERT INTO products (sku, category_id, price, discount_rate, vat_rate, stock, external_link) VALUES " . implode(',', $productValues); $productStmt = $connect->prepare($productQuery); $productStmt->execute($productParams); // 准备批量插入product_details的参数(通过sku关联product_id) $detailValues = []; $detailParams = []; foreach($file_data as $row) { $detailValues[] = "( (SELECT product_id FROM products WHERE sku = ?), ?, ? )"; $detailParams[] = $row[$_POST["sku"]]; $detailParams[] = $row[$_POST["title"]]; $detailParams[] = $row[$_POST["description"]]; } // 执行product_details批量插入 $detailQuery = " INSERT INTO product_details (product_id, title, description) VALUES " . implode(',', $detailValues); $detailStmt = $connect->prepare($detailQuery); $detailStmt->execute($detailParams); $connect->commit(); echo 'Data Imported Successfully'; } catch(PDOException $e) { $connect->rollBack(); echo 'Import Failed: ' . $e->getMessage(); } }
关键注意事项
- SQL注入防护:必须使用PDO参数绑定,绝对不能直接拼接用户输入到SQL语句中,原有代码存在严重安全漏洞。
- 事务处理:所有插入操作必须放在事务中,避免出现一张表插入成功、另一张失败的不一致情况。
- 关联约束:确保
product_details表存在product_id外键字段,关联products表的主键;如果用sku关联,必须给products.sku添加唯一约束。 - 字段类型匹配:插入的数据类型要和数据库表定义一致(比如
price是数值类型,PDO会自动处理类型转换)。
内容的提问来源于stack exchange,提问作者Marcin Wierzycki
相关产品推荐
相关产品推荐

