根据2025年7月的最新开发者调查显示,PHP仍然是Web开发中最受欢迎的后端语言之一,尤其在中小型项目中占据主导地位,随着PHP 8.4的发布,数据库操作性能提升了约15%,使得数据导入导出这类批量操作更加高效,许多企业正在将老旧系统迁移到新版PHP平台,数据迁移需求激增,掌握数据库导入导出技能变得尤为重要。
老张是我们团队的后端开发,上周差点因为一个数据迁移任务加班到凌晨,客户要把旧系统中的10万条用户数据迁移到新平台,老张一开始用最笨的方法——一条条读取再插入,结果跑了两个小时才完成1/10,后来我教他用批量操作的方法,整个过程只用了不到3分钟。
这就是为什么每个PHP开发者都应该熟练掌握数据库导入导出技巧,无论是系统迁移、数据备份,还是定期报表生成,这些场景都离不开高效的数据操作。
在开始之前,我们先确保数据库连接没问题,现在推荐使用PDO方式,它比老式的mysql_系列函数安全得多,而且支持多种数据库。
<?php $host = 'localhost'; $dbname = 'your_database'; $username = 'your_username'; $password = 'your_password'; try { $pdo = new PDO("mysql:host=$host;dbname=$dbname", $username, $password); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); echo "连接成功!"; } catch(PDOException $e) { echo "连接失败: " . $e->getMessage(); } ?>
记住一定要设置错误模式为ERRMODE_EXCEPTION,这样出错时我们能及时发现问题。
CSV是最通用的数据交换格式,几乎所有系统都能识别,下面是一个完整的导出例子:
<?php // 连接数据库... (使用上面的连接代码) header('Content-Type: text/csv; charset=utf-8'); header('Content-Disposition: attachment; filename=users_export_'.date('Y-m-d').'.csv'); $output = fopen('php://output', 'w'); // 写入CSV标题行 fputcsv($output, array('ID', '用户名', '邮箱', '注册时间')); // 查询数据 $stmt = $pdo->query("SELECT id, username, email, register_date FROM users"); while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { fputcsv($output, $row); } fclose($output); exit; ?>
这个脚本会直接把CSV文件发送给浏览器下载,如果你需要先保存到服务器:
$filename = 'exports/users_'.date('Ymd_His').'.csv'; $file = fopen($filename, 'w'); // 其余代码类似,只是把'php://output'改为$filename
虽然CSV够用,但有时客户非要Excel格式,我们可以用PhpSpreadsheet这个强大的库:
require 'vendor/autoload.php'; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; $spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet(); 行 $sheet->setCellValue('A1', 'ID'); $sheet->setCellValue('B1', '用户名');.. // 查询并填充数据 $stmt = $pdo->query("SELECT * FROM users"); $rowNumber = 2; // 从第二行开始 while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { $sheet->setCellValue('A'.$rowNumber, $row['id']); $sheet->setCellValue('B'.$rowNumber, $row['username']); // 其他字段... $rowNumber++; } // 保存文件 $writer = new Xlsx($spreadsheet); $filename = 'users_export.xlsx'; $writer->save($filename); // 或者直接输出下载 header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); header('Content-Disposition: attachment;filename="'.$filename.'"'); $writer->save('php://output');
假设我们有一个包含用户数据的CSV文件要导入到数据库:
<?php // 连接数据库... if ($_FILES['csv_file']['error'] == UPLOAD_ERR_OK) { $tmpName = $_FILES['csv_file']['tmp_name']; // 检查是否是有效的CSV文件 if (($handle = fopen($tmpName, 'r')) !== FALSE) { // 跳过标题行(如果有) fgetcsv($handle); // 准备预处理语句 $stmt = $pdo->prepare("INSERT INTO users (username, email, password) VALUES (?, ?, ?)"); // 开始事务,提高性能 $pdo->beginTransaction(); while (($data = fgetcsv($handle)) !== FALSE) { // 假设CSV列顺序:username, email, password $username = $data[0]; $email = $data[1]; $password = password_hash($data[2], PASSWORD_DEFAULT); $stmt->execute([$username, $email, $password]); } // 提交事务 $pdo->commit(); fclose($handle); echo "成功导入数据!"; } } else { echo "文件上传出错: ".$_FILES['csv_file']['error']; } ?>
关键点:
当处理几十MB以上的CSV文件时,内存可能成为问题,这时可以这样优化:
// 在循环中添加计数器,每1000条提交一次 $counter = 0; $batchSize = 1000; while (($data = fgetcsv($handle)) !== FALSE) { // ...处理数据 $stmt->execute([...]); $counter++; if ($counter % $batchSize == 0) { $pdo->commit(); $pdo->beginTransaction(); } }
使用PhpSpreadsheet读取Excel:
require 'vendor/autoload.php'; use PhpOffice\PhpSpreadsheet\IOFactory; $inputFileName = $_FILES['excel_file']['tmp_name']; $spreadsheet = IOFactory::load($inputFileName); $sheet = $spreadsheet->getActiveSheet(); $highestRow = $sheet->getHighestRow(); $pdo->beginTransaction(); $stmt = $pdo->prepare("INSERT INTO products (name, price, stock) VALUES (?, ?, ?)"); for ($row = 2; $row <= $highestRow; $row++) { $name = $sheet->getCell('A'.$row)->getValue(); $price = $sheet->getCell('B'.$row)->getValue(); $stock = $sheet->getCell('C'.$row)->getValue(); $stmt->execute([$name, $price, $stock]); } $pdo->commit();
对于超大型数据集,MySQL提供了极快的导入方式:
$csvFile = '/path/to/your/file.csv'; $query = " LOAD DATA INFILE '".$csvFile."' INTO TABLE users FIELDS TERMINATED BY ',' ENCLOSED BY '\"' LINES TERMINATED BY '\\n' IGNORE 1 LINES (username, email, password) "; $pdo->exec($query);
注意:
大数据导出时可以压缩节省带宽:
// 创建ZIP文件 $zip = new ZipArchive(); $zipFilename = 'export_'.date('Ymd').'.zip'; if ($zip->open($zipFilename, ZipArchive::CREATE) === TRUE) { // 先导出CSV $csvFilename = 'temp_export.csv'; // ...CSV导出代码 $zip->addFile($csvFilename, 'users_export.csv'); $zip->close(); // 发送给浏览器 header('Content-Type: application/zip'); header('Content-Disposition: attachment; filename="'.$zipFilename.'"'); readfile($zipFilename); // 清理临时文件 unlink($csvFilename); unlink($zipFilename); }
长时间操作时显示进度很友好:
// 导出时 $total = $pdo->query("SELECT COUNT(*) FROM users")->fetchColumn(); $processed = 0; while ($row = $stmt->fetch()) { // 处理数据... $processed++; // 每处理100条或1%更新进度 if ($processed % 100 == 0 || $processed / $total * 100 > $lastPercent + 1) { updateProgress($processed / $total * 100); } } function updateProgress($percent) { // 可以写入数据库、文件或APC等缓存 file_put_contents('progress.txt', $percent); }
前端可以用AJAX定期读取progress.txt显示进度条。
// 检查文件扩展名和MIME类型 $allowed = ['text/csv', 'application/vnd.ms-excel']; if (!in_array($_FILES['file']['type'], $allowed)) { die("只允许上传CSV或Excel文件"); } // 检查文件扩展名 $ext = pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION); if (!in_array(strtolower($ext), ['csv', 'xlsx'])) { die("无效文件扩展名"); }
// 示例:验证邮箱 if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { // 记录错误或跳过该行 continue; }
$errorLog = []; while (...) { try { $stmt->execute([...]); } catch (PDOException $e) { $errorLog[] = [ 'row' => $rowData, 'error' => $e->getMessage() ]; continue; } } // 最后可以生成错误报告 if (!empty($errorLog)) { $errorReport = json_encode($errorLog, JSON_PRETTY_PRINT); file_put_contents('import_errors.json', $errorReport); }
假设我们要开发一个用户数据迁移功能,允许管理员上传Excel文件导入用户,也能导出当前用户数据。
完整代码示例:
<?php // database.php - 数据库连接 function getPDO() { static $pdo; if (!$pdo) { $pdo = new PDO(DB_DSN, DB_USER, DB_PASS); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } return $pdo; } // export.php - 导出功能 if (isset($_GET['action']) && $_GET['action'] == 'export') { $pdo = getPDO(); header('Content-Type: text/csv; charset=utf-8'); header('Content-Disposition: attachment; filename=users_export_'.date('Y-m-d').'.csv'); $output = fopen('php://output', 'w'); // 标题行 fputcsv($output, ['ID', '用户名', '邮箱', '注册日期', '最后登录']); // 查询数据 $stmt = $pdo->query(" SELECT id, username, email, register_date, last_login FROM users ORDER BY register_date DESC "); while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { fputcsv($output, $row); } fclose($output); exit; } // import.php - 导入功能 if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES['user_import'])) { require 'vendor/autoload.php'; $file = $_FILES['user_import']['tmp_name']; $pdo = getPDO(); try { $spreadsheet = IOFactory::load($file); $sheet = $spreadsheet->getActiveSheet(); $highestRow = $sheet->getHighestRow(); $success = 0; $errors = []; $pdo->beginTransaction(); $stmt = $pdo->prepare(" INSERT INTO users (username, email, password, register_date) VALUES (?, ?, ?, NOW()) ON DUPLICATE KEY UPDATE email = VALUES(email) "); for ($row = 2; $row <= $highestRow; $row++) { $username = $sheet->getCell('A'.$row)->getValue(); $email = $sheet->getCell('B'.$row)->getValue(); $password = $sheet->getCell('C'.$row)->getValue(); if (empty($username) || empty($email)) { $errors[] = "第 {$row} 行: 用户名和邮箱不能为空"; continue; } if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { $errors[] = "第 {$row} 行: 邮箱格式无效"; continue; } $hashedPassword = password_hash($password, PASSWORD_DEFAULT); try { $stmt->execute([$username, $email, $hashedPassword]); $success++; } catch (PDOException $e) { $errors[] = "第 {$row} 行: ".$e->getMessage(); } } $pdo->commit(); // 显示结果 $message = "成功导入 {$success} 条记录"; if (!empty($errors)) { $message .= ",有 ".count($errors)." 条失败"; $_SESSION['import_errors'] = $errors; } $_SESSION['import_message'] = $message; header('Location: users.php'); exit; } catch (Exception $e) { $pdo->rollBack(); $_SESSION['import_message'] = "导入失败: ".$e->getMessage(); header('Location: users.php'); exit; } } ?>
前端表单示例:
<!-- 导入表单 --> <form method="post" enctype="multipart/form-data" action="import.php"> <h3>导入用户数据</h3> <p>请上传Excel文件(第一行应为标题行)</p> <input type="file" name="user_import" accept=".xlsx,.xls"> <button type="submit">开始导入</button> </form> <!-- 导出按钮 --> <a href="users.php?action=export" class="btn">导出用户数据(CSV)</a> <!-- 显示导入结果 --> <?php if (!empty($_SESSION['import_message'])): ?> <div class="alert"> <?php echo $_SESSION['import_message']; ?> <?php unset($_SESSION['import_message']); ?> <?php if (!empty($_SESSION['import_errors'])): ?> <details> <summary>查看错误详情</summary> <ul> <?php foreach ($_SESSION['import_errors'] as $error): ?> <li><?php echo htmlspecialchars($error); ?></li> <?php endforeach; ?> </ul> </details> <?php unset($_SESSION['import_errors']); ?> <?php endif; ?> </div> <?php endif; ?>
Q1: 导入大量数据时内存不足怎么办?
A: 可以尝试以下方法:
ini_set('memory_limit', '512M');
fgetcsv()
逐行读取而非全部加载到内存Q2: 如何导出特定条件的数据?
A: 只需在SQL查询中添加WHERE条件即可。
// 导出最近30天活跃用户 $stmt = $pdo->prepare(" SELECT * FROM users WHERE last_login > DATE_SUB(NOW(), INTERVAL 30 DAY) ");
Q3: 导入时遇到重复数据怎么处理?
A: 有几种策略:
INSERT IGNORE
跳过重复ON DUPLICATE KEY UPDATE
更新现有记录Q4: 导出的CSV在Excel中乱码怎么办?
A: 这是编码问题,可以:
header('Content-Type: text/csv; charset=utf-8');
本文由 赛雪珍 于2025-07-31发表在【云服务器提供商】,文中图片由(赛雪珍)上传,本平台仅提供信息存储服务;作者观点、意见不代表本站立场,如有侵权,请联系我们删除;若有图片侵权,请您准备原始证明材料和公证书后联系我方删除!
本文链接:https://vps.7tqx.com/wenda/498268.html
发表评论