
在web开发中,从csv(逗号分隔值)文件导入数据到关系型数据库(如mysql)是一个常见的需求。这通常涉及到文件上传、解析csv内容,然后将解析后的数据批量插入到数据库表中。本教程将使用php的pdo(php data objects)扩展来演示这一过程,pdo提供了一个轻量级、一致的接口来访问多种数据库,并且支持预处理语句,这对于数据导入的安全性至关重要。
在开始导入数据之前,首先需要建立PHP与MySQL数据库的连接。PDO提供了一种安全且灵活的方式来完成此操作。
<?php
$dbHost = 'localhost'; // 数据库主机名
$dbName = 'test'; // 数据库名称
$dbChar = 'utf8mb4'; // 数据库字符集,推荐使用utf8mb4支持更广泛的字符
$dbUser = 'root'; // 数据库用户名
$dbPass = ''; // 数据库密码
try {
// 构造DSN (Data Source Name)
$dsn = "mysql:host={$dbHost};dbname={$dbName};charset={$dbChar}";
// 创建PDO实例,并设置错误模式为异常
$pdo = new PDO($dsn, $dbUser, $dbPass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // 抛出异常
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // 默认获取关联数组
PDO::ATTR_EMULATE_PREPARES => false, // 禁用模拟预处理(推荐)
]);
// echo "数据库连接成功!"; // 调试用
} catch (PDOException $ex) {
// 捕获PDO连接异常
exit("数据库连接失败: " . $ex->getMessage());
}
?>注意事项:
导入数据的第一步是读取并解析CSV文件。PHP提供了fgetcsv()函数,可以方便地从文件中逐行读取CSV数据。
假设我们的CSV文件名为 data.csv,内容如下:
立即学习“PHP免费学习笔记(深入)”;
BGYR002217;FK-066 BGYR002218;FK-140
请注意,此CSV文件使用分号(;)作为字段分隔符,而不是默认的逗号(,)。
// 假设文件是通过表单上传的,这里使用 $_FILES["upcsv"]["tmp_name"]
// 在实际应用中,你需要确保文件上传逻辑已正确处理
$csvFilePath = $_FILES["upcsv"]["tmp_name"] ?? null;
if (!$csvFilePath || !file_exists($csvFilePath)) {
exit("未找到上传的CSV文件或文件路径无效。");
}
// 以只读模式打开CSV文件
$fh = fopen($csvFilePath, "r");
if ($fh === false) {
exit("无法打开上传的CSV文件。");
}关键点:指定CSV分隔符fgetcsv() 函数默认使用逗号作为分隔符。如果你的CSV文件使用其他字符(如分号、制表符),你需要明确指定它。否则,fgetcsv() 会将整行内容视为一个字段,导致后续的数组索引访问错误(如Undefined array key 1)。
// ... (接上面的文件打开代码)
// 准备SQL插入语句,假设目标表名为 'users',包含 'szam' 和 'forras_szam' 两列
// 注意:VALUES后面的占位符必须是问号 (?),而不是分号问号 (?;?)
$stmt = $pdo->prepare("INSERT INTO users (szam, forras_szam) VALUES (?, ?)");
$importedRows = 0;
while (($row = fgetcsv($fh, 0, ';')) !== false) { // 关键:指定分号为分隔符
// 检查行数据是否有效,防止空行或格式错误导致的问题
if (empty($row) || !isset($row[0]) || !isset($row[1])) {
echo "跳过无效行: " . implode(';', $row) . "<br>";
continue;
}
try {
// 执行预处理语句,绑定CSV行中的数据
$stmt->execute([$row[0], $row[1]]);
$importedRows++;
} catch (PDOException $ex) {
// 捕获SQL执行异常
echo "导入数据失败 (行: " . implode(';', $row) . "): " . $ex->getMessage() . "<br>";
}
}
// 关闭文件句柄
fclose($fh);
echo "数据导入完成。成功导入 " . $importedRows . " 行。";这是数据导入的核心部分,也是原问题中出现错误的地方。
原代码中SQL语句的占位符写法为 VALUES (?;?),这是错误的。PDO预处理语句的匿名占位符必须是单个问号 ?。正确的写法应该是 VALUES (?,?)。
错误示例 (会导致SQL语法错误):
INSERT INTO users (szam, forras_szam) VALUES (?;?)
正确示例:
INSERT INTO users (szam, forras_szam) VALUES (?,?)
当PDO解析 VALUES (?;?) 时,它会将 ; 视为SQL语句的一部分而不是占位符的结束,从而导致语法错误 SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax;... near ';NULL)'。
结合上述所有部分,以下是一个完整的、健壮的CSV数据导入PHP脚本:
<?php
// 1. 数据库连接配置
$dbHost = 'localhost';
$dbName = 'test';
$dbChar = 'utf8mb4';
$dbUser = 'root';
$dbPass = '';
$pdo = null; // 初始化PDO变量
try {
$dsn = "mysql:host={$dbHost};dbname={$dbName};charset={$dbChar}";
$pdo = new PDO($dsn, $dbUser, $dbPass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]);
} catch (PDOException $ex) {
exit("数据库连接失败: " . $ex->getMessage());
}
// 2. 获取上传的CSV文件路径
// 假设这是通过HTML表单上传的,表单字段名为 'upcsv'
// 示例HTML表单: <form action="import.php" method="post" enctype="multipart/form-data"><input type="file" name="upcsv"><button type="submit">上传</button></form>
$csvFilePath = $_FILES["upcsv"]["tmp_name"] ?? null;
if (!$csvFilePath || !file_exists($csvFilePath)) {
exit("错误:未找到上传的CSV文件或文件路径无效。请确保文件已成功上传。");
}
// 3. 打开CSV文件
$fh = fopen($csvFilePath, "r");
if ($fh === false) {
exit("错误:无法打开上传的CSV文件。");
}
// 4. 准备SQL插入语句
// 目标表: users, 列: szam, forras_szam
// 注意:VALUES (?, ?) 是正确的占位符语法
$stmt = null; // 初始化语句对象
try {
$stmt = $pdo->prepare("INSERT INTO users (szam, forras_szam) VALUES (?, ?)");
} catch (PDOException $ex) {
fclose($fh); // 准备语句失败也要关闭文件
exit("SQL语句预处理失败: " . $ex->getMessage());
}
// 5. 逐行读取CSV并插入数据
$importedRows = 0;
$lineNum = 0; // 用于跟踪行号,便于错误定位
while (($row = fgetcsv($fh, 0, ';')) !== false) { // 关键:指定分号为分隔符
$lineNum++;
// 假设CSV文件的第一行是标题行,可以跳过
// if ($lineNum === 1) {
// continue;
// }
// 数据验证:确保每行包含预期数量的列
if (count($row) < 2) {
echo "警告:第 {$lineNum} 行数据格式不正确,跳过。内容: " . implode(';', $row) . "<br>";
continue;
}
// 清理和验证数据(根据实际需求进行更严格的验证)
$szam = trim($row[0]);
$forras_szam = trim($row[1]);
// 示例:简单的数据长度检查
if (empty($szam) || empty($forras_szam)) {
echo "警告:第 {$lineNum} 行数据包含空值,跳过。内容: " . implode(';', $row) . "<br>";
continue;
}
try {
// 执行预处理语句,绑定参数
$stmt->execute([$szam, $forras_szam]);
$importedRows++;
} catch (PDOException $ex) {
echo "错误:第 {$lineNum} 行数据导入失败。内容: " . implode(';', $row) . "。原因: " . $ex->getMessage() . "<br>";
// 可以选择在这里继续处理下一行或中断导入
}
}
// 6. 关闭文件句柄
fclose($fh);
echo "数据导入完成。成功导入 {$importedRows} 行数据。";
?>// 开启事务
$pdo->beginTransaction();
try {
// ... 逐行插入数据的循环 ...
$stmt = $pdo->prepare("INSERT INTO users (szam, forras_szam) VALUES (?, ?)");
while (($row = fgetcsv($fh, 0, ';')) !== false) {
// ... 数据处理和验证 ...
$stmt->execute([$szam, $forras_szam]);
}
// 提交事务
$pdo->commit();
echo "所有数据已成功导入并提交。";
} catch (PDOException $ex) {
// 回滚事务
$pdo->rollBack();
echo "数据导入失败,已回滚所有操作。原因: " . $ex->getMessage();
}通过本教程,我们详细讲解了如何利用PHP PDO将CSV数据安全、高效地导入到MySQL数据库。核心要点包括:正确配置PDO连接、准确处理CSV文件(特别是分隔符)、以及最关键的——使用正确的PDO预处理语句占位符语法 VALUES (?,?)。遵循这些指导原则和最佳实践,将有助于你构建健壮、可靠的数据导入功能。
以上就是将CSV文件导入MySQL数据库:使用PHP PDO的完整指南的详细内容,更多请关注php中文网其它相关文章!
PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号