原文「逐欄另存 txt 再 PHP 迴圈 INSERT」極度低效且易出錯。標準作法:
CREATE TABLE `excel_import` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`col_a` VARCHAR(100) NOT NULL DEFAULT '',
`col_b` VARCHAR(100) NOT NULL DEFAULT '',
`col_c` VARCHAR(100) NOT NULL DEFAULT '',
`col_d` VARCHAR(100) NOT NULL DEFAULT '',
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
LOAD DATA LOCAL INFILE '/path/to/data.csv'
INTO TABLE `excel_import`
CHARACTER SET utf8mb4
FIELDS TERMINATED BY ','
OPTIONALLY ENCLOSED BY '"'
ESCAPED BY '\\'
LINES TERMINATED BY '\r\n' -- Windows CSV 通常 CRLF
IGNORE 1 LINES -- 跳過標題行
(`col_a`, `col_b`, `col_c`, `col_d`);
必要條件:MySQL 伺服器與客戶端皆啟用local_infile=1;PHP PDO 連線需加PDO::MYSQL_ATTR_LOCAL_INFILE => true。
<?php
function importCsvToMysql(string $csvPath, PDO $pdo, string $table, array $columns, int $batchSize = 1000): int
{
$handle = fopen($csvPath, 'r');
if (!$handle) throw new RuntimeException("無法開啟 $csvPath");
// 讀取標題行對應欄位 (可選)
$header = fgetcsv($handle);
$colMap = array_flip($header); // 欄位名 => 索引
$placeholders = implode(',', array_fill(0, count($columns), '?'));
$sql = "INSERT INTO `$table` (`" . implode('`,`', $columns) . "`) VALUES ($placeholders)";
$stmt = $pdo->prepare($sql);
$imported = 0;
$batch = [];
while (($row = fgetcsv($handle)) !== false) {
// 資料對應與清洗
$data = [];
foreach ($columns as $col) {
$val = $row[$colMap[$col] ?? 0] ?? '';
// 例:去除首尾空白、轉換編碼、日期格式化等
$data[] = trim($val);
}
$batch[] = $data;
if (count($batch) >= $batchSize) {
$pdo->beginTransaction();
foreach ($batch as $record) {
$stmt->execute($record);
}
$pdo->commit();
$imported += count($batch);
$batch = [];
}
}
// 餘數
if ($batch) {
$pdo->beginTransaction();
foreach ($batch as $record) $stmt->execute($record);
$pdo->commit();
$imported += count($batch);
}
fclose($handle);
return $imported;
}
// 使用
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'user', 'pass', [
PDO::MYSQL_ATTR_LOCAL_INFILE => true, // 關鍵!
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);
$count = importCsvToMysql('data.csv', $pdo, 'excel_import', ['col_a','col_b','col_c','col_d']);
echo "成功匯入 $count 筆";
?>
| 工具 | 特點 | 適用場景 |
|---|---|---|
| MySQL Workbench | GUI 匯入精靈、欄位對應、預覽 | 一次性、中小檔案 |
| phpMyAdmin | Web 介面、支援 CSV/CSV.gz | 無 CLI 權限環境 |
| csvkit (csvsql) | 命令列、自動建表、型別推斷 | `csvsql --db mysql://user:pass@host/db --insert data.csv` |
| Laravel Excel (Maatwebsite) | PHP 匯入/匯出神器、Chunk 讀取、佇列、驗證 | Laravel 專案長期維護 |
| Python pandas + SQLAlchemy | 資料科學級清洗、型別推斷、大塊寫入 | 複雜 ETL、資料分析流程 |
LOAD DATA 需正確指定 LINES TERMINATED BY。fgetcsv 自動處理。Excel → MySQL 匯入,**核心是「CSV 為中介格式」+「LOAD DATA INFILE 批次載入」**。拋棄逐行 PHP INSERT 迴圈,擁抱資料庫原生批次載入能力,效能提升 100-1000 倍。配合成熟工具 (Laravel Excel, csvkit, pandas) 處理清洗轉換,才是專業資料工程態度。