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

Viewed 289

I'm trying to make a VBScript to open or get a handle of an already opened Excel workbook, and run a macro which executes a series of SQL queries against the workbook itself, using worksheets as tables. This workbook is expected to stay open by some users. The workbook act as an semi-automated report delivery vessel, which updates itself with a certain action taken in the ERP system the users use.

Problem

The scripts work fine independently but the ADO call in VBA macro fails the first time it's called if the workbook was opened by the script, returning a run-time error No value given for one or more required parameters. But running the same macro with the same parameter again, whether from the VBScript or VBA, succeeds. And if the file has already been opened manually then the macro succeeds every time. Below summarizes the scenarios.

  1. Excel instance exists and the desired workbook has already been opened by hand (File double clicked or through Excel GUI File->Open) - Get a handle with GetObject and run macro => succeeds every time

  2. Excel instance exists but the desired workbook isn't open - Open the book using the existing app handle then run macro => the macro errors the first run. Succeeds on subsequent runs

  3. Excel instance does not exist - Create app/workbook objects, open the workbook then run macro => the macro errors the first run. Succeeds on subsequent runs


Some Considerations

  • In all scenarios above, cn.Status = 1 at the time of error.
  • SQL statement that doesn't use JOIN can succeed on the first run. All statements with JOIN fail on the first run.

Processes

  1. VBS is triggered when a user enters a key value in a certain ERP task
  2. VBS sends the key value to the workbook as a parameter for macro
  3. Macro does the following in the order listed order
    • Execute RefreshAll to update tables with saved connections
    • Execute queries against worksheets using the parameter <--this is where it fails only on the first run.
    • Produce properly formatted report

What am I missing in making these codes work?

Macro: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

FileOpener: 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" 
1 Answers

The culprit was the saved connections to the ERP system within the workbook. When Enable background refresh option is enabled, the codes that are intended to execute after "Refresh data when opening the file" or the first line of macro Thisworkbook.Refreshlall will execute without waiting for the refresh to be completed. This resulted in querying an empty table, thus producing an error message that are often associated with a query returning no result set. The connection to an integral data source had this enabled, and disabling made everything work as intended. This explained why some SQL statement with no JOIN worked while all statements with JOIN, all of which were dependent on the table with background query enabled, failed, and why the 2nd macro run always succeeded.

Additionally, though not relevant to the main issue...for those who might be considering a recursive data source method like this, I'd recommend reviewing the connection string. I had issues with incorrect results for something as basic as SELECT T1.NumberColumn FROM T1 INNER JOIN T2 ON T1.col1 = T2.Col2. For example when the expected result is 1 (since the base table shows 1 for the particular row), the actual result might be 2. This was due to the omission of IMEX=1 in the connection string to the workbook itself. This resulted in the number columns of some tables to be treated in an unexpected way.

Related