專案需將 SQL Server 資料庫完整遷移至 MySQL。經調研發現兩種主流方案:ODBC 直接匯出與腳本程式化遷移。實測後發現前者雖簡便但陷阱眾多,後者雖需編碼但掌控度高、適應性強。本文詳細記錄踩坑經驗與解決方案,供同類需求參考。
| 問題 | 表現 | 影響 |
| 大物件型別不支援 | ntext、image 欄位無法寫入 | 資料遺失,需手工處理 |
| 時間型別衝突 | 目標表用 INT 存 Unix 時間戳,來源為 DATETIME | 匯入失敗或資料錯誤 |
| 欄位型別映射不準 | 自動生成的 MySQL 表結構往往不符預期 | 後續查詢效能差、應用報錯 |
結論:僅適合結構簡單、無特殊型別、型別映射無要求的測試環境。生產環境強烈不推薦。
方案二:程式化遷移(PHP / ASP) —— 掌控每一行資料
核心優勢
- 型別轉換完全可控(如
DATETIME → Unix Timestamp)
- 可針對特殊欄位自訂處理邏輯
- 支援分批、斷點續傳、錯誤重試
- 大物件欄位可轉檔案存儲,資料庫只存路徑
前置準備
- MySQL 環境就緒
- 目標資料庫、表結構已建立(含索引、外鍵)
- SQL Server 建立 System DSN(範例名
web)
PHP 遷移腳本範例
<?php
// 連接 SQL Server (via ODBC)
$cnx = odbc_connect('web', 'admin', '123456');
$cur = odbc_exec($cnx, 'SELECT * FROM user');
// 連接 MySQL
$conn = mysql_pconnect('localhost', 'root', '123456');
mysql_select_db('mydb', $conn) or die('無法連接資料庫');
$num_row = 0;
while (odbc_fetch_row($cur)) {
$num_row++;
// 逐欄讀取(索引從 1 開始)
$field1 = odbc_result($cur, 1); // id
$field2 = odbc_result($cur, 2); // name
$field3 = odbc_result($cur, 3); // username
$field4 = odbc_result($cur, 4); // password
$field5 = odbc_result($cur, 5); // recdate (DATETIME)
// 關鍵轉換:DATETIME → Unix Timestamp (INT)
$field5 = datetime_to_timestamp($field5);
// 參數化查詢防注入(生產環境建議用 PDO + prepared statements)
$sql = sprintf(
"INSERT INTO user (id, name, username, password, recdate) VALUES ('%s','%s','%s','%s',%d)",
mysql_real_escape_string($field1),
mysql_real_escape_string($field2),
mysql_real_escape_string($field3),
mysql_real_escape_string($field4),
$field5
);
mysql_query($sql, $conn);
}
function datetime_to_timestamp($str) {
// 格式:YYYY-MM-DD HH:MM:SS
list($date, $time) = explode(' ', $str);
list($y, $m, $d) = explode('-', $date);
list($h, $i, $s) = explode(':', $time);
return mktime($h, $i, $s, $m, $d, $y);
}
?>
ASP 遷移腳本範例(含 SQL Server 端轉換)
<%
Set conn = Server.CreateObject("ADODB.Connection")
conn.Open "web", "admin", "123456"
' SQL Server 端直接用 DATEDIFF 轉 Unix Timestamp (扣除 8 小時時區)
sql = "SELECT ID, name, username, password, " & _
"DATEDIFF(s, '1970-01-01 00:00:00', recdate) - 8*3600 AS rects, " & _
"reid, filename, fileContentType, filevalue FROM senddate"
Set rs = Server.CreateObject("ADODB.Recordset")
rs.Open sql, conn, 1, 3
Set conn1 = Server.CreateObject("ADODB.Connection")
conn1.Open "myoa", "root", "q1-d6=7?"
Do While Not rs.EOF
f1 = rs(0): f2 = rs(1): f3 = rs(2): f4 = rs(3): f5 = rs(4)
sql1 = "INSERT INTO user (ID, name, username, password, recdate) " & _
"VALUES (" & f1 & ",'" & f2 & "','" & f3 & "','" & f4 & "'," & f5 & ")"
conn1.Execute sql1
rs.MoveNext
Loop
rs.Close: conn.Close: conn1.Close
%>
特殊型別處理:大物件欄位的檔案化策略
針對 ntext、image 無法直接遷移的問題,採用「落地為檔、庫存路徑」方案:
ntext → nvarchar(4000) + 截斷風險評估
- 實測原始資料長度均 < 4000 字元,改型別無資料遺失
- 若超長需分段存儲或改用
LONGTEXT
image → 檔案系統 + ASP 處理函數
Function makeattach(contentType, binaryData, idx)
' 依 MIME 判斷副檔名
Select Case contentType
Case "application/msword": ext = "doc"
Case "application/vnd.ms-excel": ext = "xls"
Case "image/jpeg": ext = "jpg"
' ... 其餘略
Case Else: ext = "bin"
End Select
If ext <> "bin" Then
Dim fso, fName, Dir, strm
Set fso = Server.CreateObject("Scripting.FileSystemObject")
fName = "attach" & idx & "." & ext
Dir = "D:\attach\"
' 寫入二進位檔案
Set strm = Server.CreateObject("ADODB.Stream")
strm.Type = 1 ' adTypeBinary
strm.Open
strm.Write binaryData
strm.SaveToFile Dir & fName, 2 ' adSaveCreateOverWrite
strm.Close
Set strm = Nothing
makeattach = fName ' 回傳檔名存入資料庫
Else
makeattach = ""
End If
End Function
遷移檢查清單與最佳實踐
| 階段 | 關鍵動作 |
| 前置 | 目標表結構先行建立、索引優化、字符集統一 (utf8mb4) |
| 過程 | 分批提交 (每 500-1000 筆 COMMIT)、記錄失敗 PK 供重跑、監控磁碟/記憶體 |
| 驗證 | 行數比對、關鍵欄位抽樣比對、MD5 校驗大欄位、應用端冒煙測試 |
| 事後 | 更新統計資訊 (ANALYZE TABLE)、重建索引、監控慢查詢 |
現代化替代工具推薦
- pt-archiver (Percona Toolkit):高效分頁遷移、支援條件過濾、可暫停恢復
- MySQL Workbench Migration Wizard:圖形化、支援異構遷移、型別映射可視化調整
- DataX / Canal + Kafka:企業級同步平台、支援增量同步、斷點續傳
- Python + SQLAlchemy + Pandas:靈活腳本化、資料清洗轉換強、單元測試友善
總結
SQL Server 遷移 MySQL 的核心難點在於型別系統差異與大物件處理。ODBC 直接匯出雖省時但不可控,程式化遷移雖需開發成本,卻能精準處理每個轉換細節,確保資料完整性與業務連續性。建議採用「腳本遷移 + 校驗腳本 + 回滾預案」的標準化流程,並優先評估現代化遷移工具降低維護負擔。
- 上一篇文章: MySQL 創辦人 David Axmark 專訪:平衡功能擴充與效能核心的哲學
- 下一篇文章: MySQL 核心資料類型深度解析:TEXT、DATE、SET 與通配符實戰
Copyright ©
程式碼之家 All Rights Reserved