如果文件以编程方式打开,则 Excel 工作簿 ADO 查询本身在第一次运行时失败

标签 excel vba vbscript

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

问题

脚本独立运行良好,但 VBA 宏中的 ADO 调用失败如果脚本打开了工作簿,第一次调用它,返回运行时错误没有给出值一个或多个必需的参数。 但是,无论是从 VBScript 还是 VBA,再次运行具有相同参数的相同宏都会成功。如果文件已经被手动打开,那么宏每次都会成功。下面总结了这些场景。

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

  2. Excel 实例存在,但所需的工作簿未打开 - 使用现有的应用程序句柄打开工作簿,然后运行宏 => 宏在第一次运行时出错。后续运行成功

  3. Excel 实例不存在 - 创建应用程序/工作簿对象,打开工作簿然后运行宏 => 第一次运行时宏出错。后续运行成功


一些注意事项

  • 在上述所有情况下,cn.Status = 1 发生错误。
  • 不使用 JOIN 的 SQL 语句可以在第一次运行时成功。所有带有 JOIN 的语句在第一次运行时都会失败。

进程

  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 的第一行之后执行的代码将在没有执行的情况下执行等待刷新完成。这导致查询一个空表,从而产生一条错误消息,该消息通常与不返回结果集的查询相关联。与完整数据源的连接启用了此功能,禁用后一切都按预期工作。这解释了为什么一些没有 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/

相关文章:

javascript - 如何访问 AngularJS 中包含空格的 EXCEL 字段

excel - 从 Excel 中打开 Word

vba - 如何在VBA中获取计算机的名称?

syntax - 了解 VBScript 中的 Const 表达式

javascript - 如何过滤文件输入,使其只显示一种类型的文件?

c# - 在 C# 中读取 excel (.xlsx) 文件

vba - 根据另一列中的条件填充上方和下方的空白单元格

excel - 消费/订阅类模块的自定义事件

excel - 如何使用 VBA 超越 Windows 对话框

asp-classic - 获取变量名 Classic ASP