Skip to content

MySQL 批量插入

当您需要使用 PHP 向 MySQL 表中插入多行时,有几种方法。现代最佳实践优先考虑安全性(防止 SQL 注入)和效率。我们将探讨使用 MySQLi 和 PDO 扩展的方法。

  • 安全性 (Security): 始终使用预处理语句(prepared statements)来防止 SQL 注入漏洞。
  • 效率 (Efficiency): 对于大量插入,在单个查询中插入多行或使用事务(transactions)比执行单个插入语句快得多。
  • 原子性 (Atomicity): 如果所有插入都必须一起成功或失败(例如,资金转账),请使用数据库事务(主要在 PDO 中演示)。

方法 1: 使用预处理语句的 MySQLi(循环)

Section titled “方法 1: 使用预处理语句的 MySQLi(循环)”

此方法只准备一次 INSERT 语句,然后使用不同的数据多次执行。它安全且相对直观。

<?php
$servername = "localhost";
$username = "your_username"; // 替换为您的数据库用户名
$password = "your_password"; // 替换为您的数据库密码
$dbname = "myDB"; // 替换为您的数据库名
// 启用 mysqli 错误报告
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
try {
// 创建连接(面向对象风格)
$conn = new mysqli($servername, $username, $password, $dbname);
// 准备 SQL 语句(防止 SQL 注入)
$stmt = $conn->prepare("INSERT INTO MyGuests (firstname, lastname, email) VALUES (?, ?, ?)");
// 要插入的数据
$guests = [
['John', 'Doe', 'john.doe@example.com'],
['Mary', 'Moe', 'mary.moe@example.com'],
['Julie', 'Dooley', 'julie.dooley@example.com']
];
echo "尝试插入记录...<br>";
// 为每位访客绑定参数并执行
foreach ($guests as $guest) {
$firstname = $guest[0];
$lastname = $guest[1];
$email = $guest[2];
// 将变量绑定到预处理语句作为参数
// 'sss' 指定参数的类型(字符串,字符串,字符串)
$stmt->bind_param("sss", $firstname, $lastname, $email);
// 执行语句
if ($stmt->execute()) {
echo "已成功为 $firstname $lastname 创建新记录。<br>";
}
// 错误处理主要由以上 mysqli_report 设置覆盖
// 如果需要,可以在此处添加特定检查。
}
echo "记录插入完成。<br>";
// 关闭语句
$stmt->close();
} catch (mysqli_sql_exception $e) {
// 捕获连接或查询错误
die("数据库错误: " . $e->getMessage() . " (错误码: " . $e->getCode() . ")");
} finally {
// 如果连接已打开,请务必关闭
if (isset($conn)) {
$conn->close();
}
}
?>

方法 2: 使用事务和预处理语句的 PDO

Section titled “方法 2: 使用事务和预处理语句的 PDO”

PDO (PHP Data Objects) 提供了访问各种数据库的一致接口。使用事务可以确保所有插入都被视为一个原子单元——它们要么全部成功,要么在发生错误时全部不保存。

<?php
$servername = "localhost";
$username = "your_username"; // 替换为您的数据库用户名
$password = "your_password"; // 替换为您的数据库密码
$dbname = "myDBPDO"; // 替换为您的数据库名
try {
// 创建 PDO 连接
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
// 将 PDO 错误模式设置为异常
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// 要插入的数据
$guests = [
['Peter', 'Griffin', 'peter@example.com'],
['Lois', 'Griffin', 'lois@example.com'],
['Chris', 'Griffin', 'chris@example.com']
];
// 开启事务
$conn->beginTransaction();
echo "事务已开启。尝试插入...<br>";
// 准备 SQL 语句(在循环外以提高效率)
$stmt = $conn->prepare("INSERT INTO MyGuests (firstname, lastname, email) VALUES (:firstname, :lastname, :email)");
// 为每位访客绑定参数并执行
foreach ($guests as $guest) {
$stmt->bindParam(':firstname', $guest[0]);
$stmt->bindParam(':lastname', $guest[1]);
$stmt->bindParam(':email', $guest[2]);
$stmt->execute();
echo "正在执行 " . htmlspecialchars($guest[0]) . " 的插入...<br>";
}
// 如果所有插入都成功,则提交事务
$conn->commit();
echo "事务提交成功。所有记录已插入。<br>";
} catch (PDOException $e) {
// 如果发生错误,则回滚事务
if ($conn->inTransaction()) {
$conn->rollBack();
echo "由于错误导致事务回滚。<br>";
}
die("数据库错误: " . $e->getMessage());
} finally {
// 关闭连接
$conn = null;
}
?>

方法 3: 使用多个 VALUES 的单条 INSERT 语句(MySQLi/PDO)

Section titled “方法 3: 使用多个 VALUES 的单条 INSERT 语句(MySQLi/PDO)”

对于插入已知的中等数量的行,构建一个包含多个值集的单条 INSERT 语句可能是性能最佳的方法。但是,动态构建查询字符串和参数绑定需要谨慎处理。

<?php
// --- 连接设置同前例 (MySQLi 或 PDO) ---
// 本例使用 PDO:
$servername = "localhost";
$username = "your_username";
$password = "your_password";
$dbname = "myDBPDO";
try {
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// 要插入的数据
$guests = [
['Stewie', 'Griffin', 'stewie@example.com'],
['Brian', 'Griffin', 'brian@example.com'],
];
if (!empty($guests)) {
// 开始构建查询
$sql = "INSERT INTO MyGuests (firstname, lastname, email) VALUES ";
$placeholders = [];
$values = [];
// 为每行创建占位符 (?, ?, ?)
foreach ($guests as $guest) {
$placeholders[] = '(?, ?, ?)';
// 将访客数组展平到值数组中
array_push($values, ...$guest);
}
// 组合占位符: (?, ?, ?), (?, ?, ?), ...
$sql .= implode(', ', $placeholders);
// 准备语句
$stmt = $conn->prepare($sql);
// 使用展平后的值数组执行
$stmt->execute($values);
$count = count($guests);
echo "已成功在单个查询中插入 $count 条记录。<br>";
} else {
echo "未提供访客数据。<br>";
}
} catch (PDOException $e) {
die("数据库错误: " . $e->getMessage());
} finally {
$conn = null;
}
?>
  • 预处理语句(循环)(Prepared Statements (Loop)): 良好的通用方法,安全,易于理解。
  • PDO 事务 (PDO Transactions): 在需要原子性(全部成功或全部失败)时最佳。相较于自动提交模式,也可以提高大量插入的性能。
  • 单条多值 INSERT (Single Multi-Value INSERT): 对于已知行数集合通常最快,但需要更复杂的查询构建。

已弃用方法(应避免):mysqli_multi_query()

Section titled “已弃用方法(应避免):mysqli_multi_query()”

mysqli_multi_query() 允许在单个字符串中执行连接的多条 SQL 语句,但强烈不推荐将其用于插入数据,除非您绝对确定所有数据都已正确转义。如果涉及用户输入且未极其谨慎处理,则存在重大的 SQL 注入风险。预处理语句是更安全且通常首选的方法。