我试图制作一个 VBScript 来打开或获取一个已打开的 Excel 工作簿的句柄,并运行一个宏,该宏使用工作表作为表格对工作簿本身执行一系列 SQL 查询。此工作簿预计将由某些用户保持打开状态。工作簿充当半自动报告交付容器,它会根据用户使用的 ERP 系统中采取的特定操作进行 self 更新。
问题
脚本独立运行良好,但 VBA 宏中的 ADO 调用失败如果脚本打开了工作簿,第一次调用它,返回运行时错误没有给出值一个或多个必需的参数。
但是,无论是从 VBScript 还是 VBA,再次运行具有相同参数的相同宏都会成功。如果文件已经被手动打开,那么宏每次都会成功。下面总结了这些场景。
存在 Excel 实例并且已手动打开所需的工作簿(双击文件或通过 Excel GUI 文件 -> 打开)- 使用 GetObject 获取句柄并运行宏 => 每次都成功
Excel 实例存在,但所需的工作簿未打开 - 使用现有的应用程序句柄打开工作簿,然后运行宏 => 宏在第一次运行时出错。后续运行成功
Excel 实例不存在 - 创建应用程序/工作簿对象,打开工作簿然后运行宏 => 第一次运行时宏出错。后续运行成功
一些注意事项
- 在上述所有情况下,
cn.Status = 1
发生错误。 - 不使用 JOIN 的 SQL 语句可以在第一次运行时成功。所有带有 JOIN 的语句在第一次运行时都会失败。
进程
- 当用户在某个 ERP 任务中输入键值时触发 VBS
- VBS 将键值作为宏的参数发送到工作簿
- 宏按照列出的顺序执行以下操作
- 执行 RefreshAll 以使用保存的连接更新表
- 使用参数对工作表执行查询 <-- 这是它仅在第一次运行时失败的地方。
- 生成格式正确的报告
在使这些代码起作用时我缺少什么?
宏:VBA
Option Explicit
Public cn as ADODB.Connection
Dim rs As ADODB.Recordset
Dim sSQL as string
Set cn = New ADODB.Connection
Set rs = New ADODB.Recordset
With cn
.Provider = "Microsoft.ACE.OLEDB.12.0"
.ConnectionString = "Data Source=" & ThisWorkbook.FullName & ";" & _
"Extended Properties = 'Excel 12.0 xml;HDR=YES'"
.CursorLocation = adUseClient
.Open
End With
sSQL = "SELECT t1.column1, t2.column1 FROM t1 INNER JOIN t2 ON t1.pk = t2.pk"
rs.Open strSQL, cn, adOpenStatic, adLockReadOnly
'cn.Execute (strSQL) <--- same results as using RecordSet
文件开启器:VBS
Dim oExcel, oWb
Dim blFileOpen : blFileOpen = False
Set oExcel = GetObject(, "Excel.Application") ' Check if Excel is running
Select Case IsEmpty(oExcel)
Case True 'no Excel instance. Start app and open book
Set oExcel = CreateObject("Excel.Appliation")
Set oWb = oExcel.Workbooks.Open(sPath & sFileName)
Case False 'Excel instance exists. check what's open
For Each wb In oExcel.Workbooks
If oWb.Name = sFileName Then 'File is already open. Get a handle
Set oWB = GetObject(sPath & sFileName)
blFileOpen = True
Exit Sub
End if
Next
'Excel instance exists but the target file wasn't open. Open the file.
Set oWB = oExcel.Workbooks.Open(sPath & sFileName)
End Select
oWB.Application.Visible = True
oWB.Application.Run "Macro", "Param"
最佳答案
罪魁祸首是工作簿中保存的与 ERP 系统的连接。当启用 Enable background refresh
选项时,在“打开文件时刷新数据”或宏 Thisworkbook.Refreshlall
的第一行之后执行的代码将在没有执行的情况下执行等待刷新完成。这导致查询一个空表,从而产生一条错误消息,该消息通常与不返回结果集的查询相关联。与完整数据源的连接启用了此功能,禁用后一切都按预期工作。这解释了为什么一些没有 JOIN 的 SQL 语句有效,而所有带有 JOIN 的语句都失败了,所有这些语句都依赖于启用了后台查询的表,以及为什么第二个宏运行总是成功。
此外,虽然与主要问题无关……对于那些可能正在考虑使用这样的递归数据源方法的人,我建议您查看连接字符串。对于像 SELECT T1.NumberColumn FROM T1 INNER JOIN T2 ON T1.col1 = T2.Col2.
这样的基本内容,我遇到了不正确的结果问题。例如,当预期结果为 1 时(因为基表显示1 对于特定行),实际结果可能是 2。这是由于在工作簿本身的连接字符串中省略了 IMEX=1。这导致某些表的数字列以意想不到的方式处理。
关于如果文件以编程方式打开,则 Excel 工作簿 ADO 查询本身在第一次运行时失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65503900/