問題
使用 VBA 的 ADODB 對象的 command 對象、parameter 對象,插入的中文數據為亂碼
驅動下載、安裝、引用
驅動網址(下載路徑)
使用的 ODBC 驅動(需要梯子才能下載,感謝大佬開源)
http://www.ch-werner.de/sqliteodbc/
版本選擇
根據 Office 32/64 位選擇對應版本的驅動
即:32/64 位的 Office 就安裝 32/64 位的驅動
我的電腦是 32 位 Office,則下面以 32 位驅動的配置為例
基礎配置
-
在 VBA 代碼編輯器中,點擊“工具” --> “引用”
-
勾選 ActiveX Data Object (若有多個版本建議選最新的) 、Scripting Runtime
示例代碼(出現亂碼)
數據庫定義(VBA 字符拼接的 SQL)
自增 ID 字段 + 其他 TEXT 類型數據
sql = "CREATE TABLE devList (" & _"id INTEGER PRIMARY KEY AUTOINCREMENT, " & _"deviceNumber TEXT, " & _"deviceIdentifier TEXT, " & _"deviceChineseName TEXT, " & _"deviceDrawingCode TEXT, " & _"moduleBoxNumber TEXT, " & _"stationName TEXT)"
VBA 代碼
Function demo(dbPath as string) As BooleanOn Error GoTo errorHandler' 初始化變量Dim conn As ADODB.ConnectionDim cmd As ADODB.CommandDim wb As WorkbookDim ws As WorksheetDim lastrow As LongDim stationName As String' ------------------------------- 業務相關邏輯 ------------------------------- ' 打開工作簿,工作簿文件絕對路徑(包含文件名+后綴) = FilePathSet wb = Workbooks.Open(FilePath, ReadOnly:=True)' 獲取第二個sheetSet ws = wb.Worksheets(2)stationName = ws.Name'獲取表格行數lastrow = ws.Cells(ws.Rows.count, "C").End(xlUp).row'總數減去標題行totalCount = lastrow - 2' ---------------------------- 業務相關邏輯 - 結束 ---------------------------- ' 建立連接' dbpath = SQLite 數據庫文件的絕對路徑Set conn = New ADODB.Connectionconn.Open "DRIVER={SQLite3 ODBC Driver};Database=" & dbPath & ""' 建立指令對象Set cmd = New ADODB.Command' 指令語句 ? 為占位符,之后調用 parameter 方法進行替換sql = "INSERT INTO devList (deviceNumber, deviceIdentifier, deviceChineseName, " & _"deviceDrawingCode, moduleBoxNumber, stationName) VALUES (?, ?, ?, ?, ?, ?)"With cmd.ActiveConnection = conn.CommandText = sql.CommandType = adCmdText' 數據集對象:清除之前的參數While .Parameters.count > 0.Parameters.Delete 0Wend'創建參數域.Parameters.Append .CreateParameter("p1", adVarChar, adParamInput, 255, "P1") 'C 設備編號.Parameters.Append .CreateParameter("p2", adVarChar, adParamInput, 255, "P2") 'G 設備標識.Parameters.Append .CreateParameter("p3", adVarChar, adParamInput, 255, "P3") 'J 設備中文名.Parameters.Append .CreateParameter("p4", adVarChar, adParamInput, 255, "P4") 'L 設備圖紙代碼.Parameters.Append .CreateParameter("p5", adVarChar, adParamInput, 255, "P5") 'N 模塊箱編號.Parameters.Append .CreateParameter("p6", adVarChar, adParamInput, 255, "P6") '站點名.Parameters.Append .CreateParameter("p7", adVarChar, adParamInput, 255, "P7") 'H 設備安裝分區End With' 開啟事務,批量插入conn.BeginTrans' 各行數據批量插入For i = 3 To lastrow' ------------------------------- 業務相關邏輯 --------------------------' 每行各列數據與數據集對象綁定cmd("p1") = CStr(ws.Cells(i, "C").Value)cmd("p2") = CStr(ws.Cells(i, "G").Value)cmd("p3") = CStr(ws.Cells(i, "J").Value)cmd("p4") = CStr(ws.Cells(i, "L").Value)cmd("p5") = CStr(ws.Cells(i, "N").Value)cmd("p6") = stationName' ---------------------------- 業務相關邏輯 - 結束 ----------------------' 執行插入cmd.ExecuteprogressCount = progressCount + 1Application.StatusBar = "處理進度... " & progressCount & "/" & totalCountDoEventsNext i'提交事務conn.CommitTrans'執行完成,清理收尾goto CleanuperrorHandler:demo_dbProcess = FalseMsgBox "數據導入 SQLite 錯誤:" & Err.Description, vbCritical'回滾 -> 數據庫斷開 -> 對象釋放If Not conn Is Nothing Thenconn.RollbackTransIf conn.State = adStateOpen Thenconn.CloseEnd IfSet conn = NothingEnd IfExit FunctionCleanup:'正常執行完成的清理工作demo_dbProcess = Trueconn.CloseSet conn = NothingIf Not wb Is Nothing Then wb.Close FalseApplication.StatusBar = "導入完成,導入數據行數:" & totalCount
End Function
插入時發現中文變成了亂碼(類似下面的樣式,實際不是這個表,這里只是做個展示)
問題解決
使用 SQLite ODBC 注意以下問題,才能防止中文等非英文字符操作錯誤
解決方法
-
在 ODBC 連接字中使用以下參數
OEMCP=1
:數據庫驅動會自動處理字符的編碼,保證寫入正確NoWCHAR=0
:(這是驅動的默認值,可以不加)設置允許使用 WCHAR 數據類型,保證上面插入的數據在 SQLite 中顯示正確- 最終的連接字如下
"DRIVER={SQLite3 ODBC Driver};Database=<數據庫路徑>;OEMCP=1;NoWCHAR=0;"
- 注意上面的連接字對應的
數據庫路徑
中不能包含中文字符,否則會報錯connect failed
(連接失敗)
-
ADODB
的 parameter 對象,指定的變量類型應更改為雙寬類型,保證 parameter 對象傳入驅動的數據字符是正確的- SQLite 中
varchar
、TEXT
類型的變量,應定義為adVarWChar
而不是adVarChar
- SQLite 中 char 類型的變量,應定義為
adWChar
而不是adChar
- 示例代碼的修改如下
.Parameters.Append .CreateParameter("p1", adVarWChar, adParamInput, 255, "P1") .Parameters.Append .CreateParameter("p2", adVarWChar, adParamInput, 255, "P2") .Parameters.Append .CreateParameter("p3", adVarWChar, adParamInput, 255, "P3") .Parameters.Append .CreateParameter("p4", adVarWChar, adParamInput, 255, "P4") .Parameters.Append .CreateParameter("p5", adVarWChar, adParamInput, 255, "P5") .Parameters.Append .CreateParameter("p6", adVarWChar, adParamInput, 255, "P6") .Parameters.Append .CreateParameter("p7", adVarWChar, adParamInput, 255, "P7")
- SQLite 中
改進后的示例代碼(導入中文數據無亂碼)
Function demo_dbProcess(dbPath as string) As BooleanOn Error GoTo errorHandler' 初始化變量Dim conn As ADODB.ConnectionDim cmd As ADODB.CommandDim wb As WorkbookDim ws As WorksheetDim lastrow As LongDim stationName As String' ------------------------------- 業務相關邏輯 ------------------------------- ' 打開工作簿,工作簿文件絕對路徑(包含文件名+后綴) = FilePathSet wb = Workbooks.Open(FilePath, ReadOnly:=True)' 獲取第二個sheetSet ws = wb.Worksheets(2)stationName = ws.Name'獲取表格行數lastrow = ws.Cells(ws.Rows.count, "C").End(xlUp).row'總數減去標題行totalCount = lastrow - 2' ---------------------------- 業務相關邏輯 - 結束 ---------------------------- ' 建立連接' dbpath = SQLite 數據庫文件的絕對路徑Set conn = New ADODB.Connectionconn.Open "DRIVER={SQLite3 ODBC Driver};Database=" & dbPath & ";OEMCP=1;NoWCHAR=0;"' 建立指令對象Set cmd = New ADODB.Command' 指令語句 ? 為占位符,之后調用 parameter 方法進行替換sql = "INSERT INTO devList (deviceNumber, deviceIdentifier, deviceChineseName, " & _"deviceDrawingCode, moduleBoxNumber, stationName) VALUES (?, ?, ?, ?, ?, ?)"With cmd.ActiveConnection = conn.CommandText = sql.CommandType = adCmdText' 數據集對象:清除之前的參數While .Parameters.count > 0.Parameters.Delete 0Wend'創建參數域.Parameters.Append .CreateParameter("p1", adVarWChar, adParamInput, 255, "P1") 'C 設備編號.Parameters.Append .CreateParameter("p2", adVarWChar, adParamInput, 255, "P2") 'G 設備標識.Parameters.Append .CreateParameter("p3", adVarWChar, adParamInput, 255, "P3") 'J 設備中文名.Parameters.Append .CreateParameter("p4", adVarWChar, adParamInput, 255, "P4") 'L 設備圖紙代碼.Parameters.Append .CreateParameter("p5", adVarWChar, adParamInput, 255, "P5") 'N 模塊箱編號.Parameters.Append .CreateParameter("p6", adVarWChar, adParamInput, 255, "P6") '站點名.Parameters.Append .CreateParameter("p7", adVarWChar, adParamInput, 255, "P7") 'H 設備安裝分區End With' 開啟事務,批量插入conn.BeginTrans' 各行數據批量插入For i = 3 To lastrow' ------------------------------- 業務相關邏輯 ------------------------------- ' 每行各列數據與數據集對象綁定cmd("p1") = CStr(ws.Cells(i, "C").Value)cmd("p2") = CStr(ws.Cells(i, "G").Value)cmd("p3") = CStr(ws.Cells(i, "J").Value)cmd("p4") = CStr(ws.Cells(i, "L").Value)cmd("p5") = CStr(ws.Cells(i, "N").Value)cmd("p6") = stationName' ---------------------------- 業務相關邏輯 - 結束 ---------------------------- ' 執行插入cmd.Execute'進度顯示progressCount = progressCount + 1Application.StatusBar = "處理進度... " & progressCount & "/" & totalCountDoEventsNext i'提交事務conn.CommitTrans'執行完成,清理收尾goto CleanuperrorHandler:demo_dbProcess = FalseMsgBox "數據導入 SQLite 錯誤:" & Err.Description, vbCritical'回滾 -> 數據庫斷開 -> 對象釋放If Not conn Is Nothing Thenconn.RollbackTransIf conn.State = adStateOpen Thenconn.CloseEnd IfSet conn = NothingEnd IfExit FunctionCleanup:'正常執行完成的清理工作demo_dbProcess = Trueconn.CloseSet conn = NothingIf Not wb Is Nothing Then wb.Close FalseApplication.StatusBar = "導入完成,導入數據行數:" & totalCount
End Function