返回
PDO批量插入多行数据:正确姿势与性能优化
mysql
2025-03-21 19:10:42
一次性插入多行数据:PDO 批量插入的正确姿势
遇到需要一次性向数据库插入多行记录的情况,结果发现代码跑不通?别急,问题出在你构建和执行 SQL 语句的方式上。咱先直接看原始代码的问题。
原始代码的问题
原代码的意图很清楚,想要把表单提交过来的多个姓名、数字和性别数据插入到 memo
表里。 看起来挺合理,但犯了几个错误:
- SQL 注入漏洞 :直接把经过
input_checker_
函数处理后的变量拼接到 SQL 语句里,太危险了! 哪怕做了转义,也还是有被注入的风险。 - 循环内重复执行 INSERT :
foreach
循环里面,每次都执行一个单独的INSERT
语句。如果有 100 条数据,就要执行 100 次数据库操作,效率低到爆! - 变量取值错误 : 原代码中
$info
未定义,而且表单数据的键值索引也没有正确处理,$name
、$number
和$gender
的值始终都只处理一个数据.
怎么解决?——正确使用 PDO 批量插入
要高效且安全地插入多行数据,得靠 PDO 的预处理语句(Prepared Statements)和事务处理。咱分几步走:
1. 准备阶段:构建 SQL 模板
咱们先把 SQL 语句写成一个“模板”,用占位符(?
或者 :name
这样的命名占位符)来代替实际的值。像这样:
$sql = "INSERT INTO memo (name, number, gender) VALUES (?, ?, ?)";
这里有三个字段 (name, number, gender), 所以每个 VALUES 有三个 ?
.
2. 绑定参数,循环执行
预处理的精髓,就是先“准备”好 SQL 语句(prepare()
方法),然后反复“执行”它(execute()
方法),每次执行时传入不同的参数。
基础版:
<?php
$servername = "";
$username = "";
$password = "";
$dbname = "";
try {
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$stmt = $conn->prepare("INSERT INTO memo (name, number, gender) VALUES (?, ?, ?)");
// 获取提交的数据数量. 假设三个数组长度一致
$count = count($_POST["name"]);
for ($i = 0; $i < $count; $i++) {
$name = input_checker($_POST["name"][$i]);
$number = input_checker($_POST["number"][$i]);
$gender = input_checker($_POST["gender"][$i]);
$stmt->execute([$name, $number, $gender]);
}
echo "多行数据插入成功!";
}
} catch(PDOException $e) {
echo "出错了:" . $e->getMessage();
}
$conn = null;
function input_checker($data) {
$data = trim($data);
$data = stripslashes($data);
$data = htmlspecialchars($data);
return $data;
}
?>
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>">
Name 1: <input type="text" name="name[]"><br>
Name 2: <input type="text" name="name[]"><br>
Name 3: <input type="text" name="name[]"><br>
Number 1: <input type="text" name="number[]"><br>
Number 2: <input type="text" name="number[]"><br>
Number 3: <input type="text" name="number[]"><br>
Gender 1: <input type="text" name="gender[]"><br>
Gender 2: <input type="text" name="gender[]"><br>
Gender 3: <input type="text" name="gender[]"><br>
<input type="submit" name="submit" value="Submit">
</form>
原理:
$conn->prepare(...)
: 准备好 SQL 语句,并返回一个 PDOStatement 对象(这里是$stmt
)。$stmt->execute([$name, $number, $gender])
: 给占位符绑定实际的值,然后执行 SQL 语句。 这里用的?
占位符,所以传一个索引数组进去。
通过使用for
循环迭代提交数据,循环调用execute
方法实现多行数据处理.
3. 更进一步:单次 SQL 执行与事务处理
其实还可以优化!如果数据库支持,可以把多行数据合并成一个 SQL 语句,一次性执行。
进阶版:
<?php
$servername = "";
$username = "";
$password = "";
$dbname = "";
try {
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$count = count($_POST["name"]);
if ($count > 0) {
//构建多个值的SQL语句
$values = [];
$params = [];
for ($i = 0; $i < $count; $i++) {
$values[] = "(?, ?, ?)";
$params[] = input_checker($_POST["name"][$i]);
$params[] = input_checker($_POST["number"][$i]);
$params[] = input_checker($_POST["gender"][$i]);
}
$sql = "INSERT INTO memo (name, number, gender) VALUES " . implode(",", $values);
$stmt = $conn->prepare($sql);
$stmt->execute($params);
echo "多行数据插入成功!";
}
}
} catch(PDOException $e) {
echo "出错了: " . $e->getMessage();
}
$conn = null;
function input_checker($data) {
$data = trim($data);
$data = stripslashes($data);
$data = htmlspecialchars($data);
return $data;
}
?>
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>">
Name 1: <input type="text" name="name[]"><br>
Name 2: <input type="text" name="name[]"><br>
Name 3: <input type="text" name="name[]"><br>
Number 1: <input type="text" name="number[]"><br>
Number 2: <input type="text" name="number[]"><br>
Number 3: <input type="text" name="number[]"><br>
Gender 1: <input type="text" name="gender[]"><br>
Gender 2: <input type="text" name="gender[]"><br>
Gender 3: <input type="text" name="gender[]"><br>
<input type="submit" name="submit" value="Submit">
</form>
改进点解释:
- 循环构建 values 数组: 构建
(?, ?, ?)
多个占位符组成的SQL语句. implode(",", $values)
: 把多个(?, ?, ?)
用逗号连接起来, 生成最终VALUES
部分。- 一次
execute
: 将参数数组传入执行即可.
这种方式比之前的多次 execute
效率更高, 因为只需要跟数据库交互一次。
更安全的操作--事务 :
<?php
$servername = "";
$username = "";
$password = "";
$dbname = "";
try {
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$count = count($_POST["name"]);
if($count > 0) {
// 开始事务
$conn->beginTransaction();
$values = [];
$params = [];
for($i=0; $i<$count; $i++) {
$values[] = "(?, ?, ?)";
$params[] = input_checker($_POST["name"][$i]);
$params[] = input_checker($_POST["number"][$i]);
$params[] = input_checker($_POST["gender"][$i]);
}
$sql = "INSERT INTO memo (name, number, gender) VALUES " . implode(",", $values);
$stmt = $conn->prepare($sql);
$stmt->execute($params);
// 提交事务
$conn->commit();
echo "多行数据插入成功!";
}
}
} catch(PDOException $e) {
// 出错时回滚
if ($conn) {
$conn->rollBack();
}
echo "出错了: " . $e->getMessage();
}
$conn = null;
function input_checker($data) {
$data = trim($data);
$data = stripslashes($data);
$data = htmlspecialchars($data);
return $data;
}
?>
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>">
Name 1: <input type="text" name="name[]"><br>
Name 2: <input type="text" name="name[]"><br>
Name 3: <input type="text" name="name[]"><br>
Number 1: <input type="text" name="number[]"><br>
Number 2: <input type="text" name="number[]"><br>
Number 3: <input type="text" name="number[]"><br>
Gender 1: <input type="text" name="gender[]"><br>
Gender 2: <input type="text" name="gender[]"><br>
Gender 3: <input type="text" name="gender[]"><br>
<input type="submit" name="submit" value="Submit">
</form>
加入事务:
$conn->beginTransaction();
: 开启一个事务。$conn->commit();
: 所有操作都成功,就提交事务,数据才会真正写入数据库。$conn->rollBack();
: 如果中间有任何一步出错,就回滚事务,撤销所有操作,保证数据一致性。
用事务的好处很明显:保证一批操作要么全部成功,要么全部失败,不会出现一部分数据插进去了、一部分没插进去的情况.
其他补充说明
input_checker
函数 : 只是做了最基本的转义, 如果想对用户的输入数据格式有校验需求 (比如必须是数字等), 在此函数增加即可.- HTML 部分 :
name
属性用数组形式 (name[]
,number[]
,gender[]
), 这样 PHP 才能接收到多个值. 注意三个[]
的输入数量应该相等. - 命名占位符 除了
?
, 也可以使用:name
这类. 绑定时候对应调整:
$stmt->execute([':name' => $name, ':number' => $number, ':gender' => $gender]);
总之记住两点:用预处理语句 ,尽量合并 SQL 执行 。这样,你就能轻松搞定 PDO 批量插入多行数据!