Excel 如果以编程方式打开文件,则首先 运行 工作簿 ADO 查询自身失败

Excel workbook ADO query to itself fails on first run if the file is opened programmatically

我正在尝试制作一个 VBScript 来打开或获取已打开的 Excel 工作簿的句柄,以及 运行 一个宏,它执行一系列 SQL 查询工作簿本身,使用工作表作为表格。此工作簿预计将由某些用户保持打开状态。工作簿充当半自动报告交付容器,它会根据用户使用的 ERP 系统中采取的特定操作进行自我更新。

问题

脚本独立运行良好,但 VBA 宏中的 ADO 调用在 第一次调用时失败,如果脚本 打开工作簿,返回 运行-time error No value given for one or more required parameters. 但是 运行再次使用相同的参数使用相同的宏,无论是来自 VBScript 还是 VBA,都会成功。如果文件已经被手动打开,那么宏每次都会成功。下面总结了这些场景。

  1. Excel 实例存在并且所需的工作簿已被手动打开(双击文件或通过 Excel GUI 文件 -> 打开)- 使用 GetObject 获取句柄和 运行 宏 => 每次都成功

  2. Excel 实例存在但所需的工作簿未打开 - 使用现有的应用程序句柄打开工作簿,然后 运行 宏 => 宏第一个错误 运行。在随后的 运行s

    上成功
  3. Excel 实例不存在 - 创建 app/workbook 对象,打开工作簿然后 运行 宏 => 第一个宏错误运行。在随后的 运行s

    上成功

一些注意事项

进程

  1. 用户在某个ERP任务中输入键值时触发VBS
  2. VBS 将键值作为宏的参数发送到工作簿
  3. 宏按照列出的顺序执行以下操作
    • 执行 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第一行之后执行的代码将在不等待刷新完成的情况下执行。这导致查询一个空的 table,从而产生一条错误消息,该消息通常与不返回结果集的查询相关联。与完整数据源的连接启用了此功能,禁用后一切都按预期工作。这解释了为什么一些没有 JOIN 的 SQL 语句有效,而所有带有 JOIN 的语句都依赖于启用后台查询的 table,失败,以及为什么第二个宏 运行 总是成功了。

此外,虽然与主要问题无关...对于那些可能正在考虑像这样的递归数据源方法的人,我建议查看连接字符串。对于像 SELECT T1.NumberColumn FROM T1 INNER JOIN T2 ON T1.col1 = T2.Col2. 这样基本的东西,我遇到了不正确的结果问题。例如,当预期结果为 1 时(因为基础 table 对特定行显示 1),实际结果可能是 2。这是由于在工作簿本身的连接字符串中遗漏了 IMEX=1。这导致某些 table 的数字列以意外方式处理。