问题
使用 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