早期 ASP 系統常使用 Access (`.mdb`) 做資料儲存,遷移至 PHP + MySQL 時需將資料導出。本文提供在 Windows 環境下,利用 PHP COM 元件操作 ADODB 連接 Access、逐筆讀取、編碼轉換 (GBK/Big5 → UTF-8)、批次寫入 MySQL 的完整方案。
php_com_dotnet.dll 擴充 (`extension=com_dotnet`)<?php
set_time_limit(0);
ini_set('memory_limit', '512M');
// ==================== 1. 連接 MySQL ====================
$myHost = 'localhost:3306';
$myUser = 'root';
$myPass = 'password';
$myDb = 'target_db';
$myPrefix = 'sdb_'; // 表前綴
$myLink = mysqli_connect($myHost, $myUser, $myPass, $myDb);
if (!$myLink) die('MySQL 連線失敗:' . mysqli_connect_error());
mysqli_set_charset($myLink, 'utf8mb4');
// ==================== 2. 連接 Access (COM ADODB) ====================
$mdbPath = realpath('data.mdb'); // 絕對路徑
if (!$mdbPath || !file_exists($mdbPath)) die('MDB 檔案不存在:' . $mdbPath);
$conn = new COM('ADODB.Connection') or die('無法建立 ADODB.Connection');
$connStr = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=$mdbPath;";
// 舊版 .mdb 可用:Provider=Microsoft.Jet.OLEDB.4.0;
try {
$conn->Open($connStr);
} catch (com_exception $e) {
die('Access 開啟失敗:' . $e->getMessage() . ' (請確認已安裝 Access Database Engine)');
}
$rs = new COM('ADODB.Recordset');
// 查詢需遷移的資料,可加 WHERE 條件篩選
$sql = "SELECT * FROM `information` WHERE `lanmu3id` = 13";
$rs->Open($sql, $conn, 1, 1); // adOpenKeyset, adLockReadOnly
// ==================== 3. 逐筆遷移 ====================
$batchSize = 500; // 批次大小
$batch = [];
$count = 0;
while (!$rs->EOF) {
// 取欄位值並轉碼 (Access 常為 GB2312/Big5)
$id = iconv('GB2312', 'UTF-8//IGNORE', $rs->Fields('ID')->value);
$title = iconv('GB2312', 'UTF-8//IGNORE', $rs->Fields('title')->value);
$summary = iconv('GB2312', 'UTF-8//IGNORE', $rs->Fields(1)->value); // 索引也可
$content = iconv('GB2312', 'UTF-8//IGNORE', $rs->Fields('content')->value);
$download = iconv('GB2312', 'UTF-8//IGNORE', $rs->Fields('download')->value);
// 組裝 INSERT (使用預處理防注入、效能更好)
$batch[] = "('$id', '$category_id', '$title', '$content', UNIX_TIMESTAMP(), 1, NULL, NULL, NULL, NULL, 'false')";
$rs->MoveNext();
$count++;
// 達批次大小執行
if (count($batch) >= $batchSize) {
$sql = "INSERT INTO `{$myPrefix}articles`
(`article_id`,`node_id`,`title`,`content`,`uptime`,`ifpub`,`align`,`filetype`,`filename`,`orderlist`,`disabled`)
VALUES " . implode(',', $batch);
if (!mysqli_query($myLink, $sql)) {
error_log('批次寫入失敗:' . mysqli_error($myLink) . "\nSQL: $sql");
}
$batch = [];
echo "已處理 $count 筆...
";
flush(); // 即時輸出進度
}
}
// 餘數寫入
if ($batch) {
$sql = "INSERT INTO `{$myPrefix}articles` ... VALUES " . implode(',', $batch);
mysqli_query($myLink, $sql);
}
echo "
遷移完成,共 $count 筆。";
// 關閉連線
$rs->Close();
$conn->Close();
mysqli_close($myLink);
?>
Microsoft.ACE.OLEDB.12.0:支援 `.accdb` (Access 2007+) 與 `.mdb`,需安裝 2010/2016 Redistributable。Microsoft.Jet.OLEDB.4.0:僅支援 `.mdb` (Access 2003 及之前),32 位元限制。建議統一用 ACE 12.0,相容性最好。
Access 內部儲存通常為 GB2312 (簡體) 或 Big5 (繁體)。使用 iconv('GB2312', 'UTF-8//IGNORE', $val):
//IGNORE:跳過無法轉換的字元,避免中斷。//TRANSLIT:嘗試近似替換 (如全形→半形)。COM 操作會拋出 com_exception,必須 try-catch 捕獲,否則腳本直接終止且無錯誤日誌。
INSERT 效能極差 (每次網路往返、事務提交)。INSERT ... VALUES (),(),() 可提升 10-50 倍速度。mysqli_prepare + bind_param 迴圈執行,更安全、可重用执行计划。// Access SQL 支援 TOP 與 WHERE 條件分頁
$pageSize = 10000;
$offset = 0;
while (true) {
$sql = "SELECT * FROM `information` WHERE `lanmu3id` = 13 ORDER BY `ID` LIMIT $offset, $pageSize";
// Access 不支援 LIMIT,改用:
// SELECT TOP $pageSize * FROM ... WHERE ID > $lastId ORDER BY ID
// 需記住上一批最後一筆 ID
// ...
if ($rs->EOF) break;
// 處理當前批次
$offset += $pageSize;
}
若環境不支援 COM (如 Linux 伺服器),可在 Windows 本機:
LOAD DATA INFILE 或 `fgetcsv` 批次導入 MySQL,速度最快、最穩定。PHP + COM + ADODB 是 Windows 環境下遷移 Access 資料最直接的方式。關鍵在於:正確安裝 ACE Provider、處理 GB2312→UTF-8 編碼、批次寫入優化效能、COM 例外捕獲。大專案建議改用「匯出 CSV → LOAD DATA INFILE」離線遷移,更穩定、可重試、不佔用 Web 執行緒。