excel - 如何等待 Power Query 刷新完成?

标签 excel vba refresh powerquery doevents

设置:

  • Windows 7(工作中)
  • Windows 10(家用)
  • Excel 2016(工作版本 4627)
  • Excel 2016(家庭内部版本 8730)
  • Power Query 设置为导入、追加和转换 Excel 文件的文件夹。此步骤有效。

按照下面所示的“我尝试过的事情:”部分中所述使用任何技术等待 Power Query 完成刷新后,可以显示一个消息框,并且可以在 Power Query 表完成之前执行任何其他代码根据“查询和连接” Pane 中显示的刷新指示器(旋转器?)图标完成更新。

上述语句的异常(exception)是 Application 类的 OnTime 方法,如下面的“代码”部分所示,该方法似乎不会中断电源查询刷新的轮询。问题在于,它使用硬编码的时间量来暂停 VBA 代码,但这并不总是有效,因为所查询的数据的大小、数量和持续时间会随着时间的推移而变化。

我尝试过:

  • 我已阅读所有说明使用 DoEventsBackgrgoundQuery = FalseCalculateUntilAsyncQueriesDone 的 StackOverflow(以及其他网站资源)方法和属性。
  • 我尝试按照此链接的建议创建 Create Before/After Query Update Events 类(下面的代码示例中未显示)。
  • 我尝试使用 Do Until/While 循环和 QueryTable 方法的 .Refreshing = True/False 属性来等待刷新完成。
  • 我尝试将 Excel 菜单中的 BackgroundQuery 属性(菜单栏 --> 数据 --> 连接 --> 属性)设置为 False,按照此处“subro”的建议:Wait until ActiveWorkbook.RefreshAll finishes - VBA,此处包含菜单图像:<

Excel menu for setting the BackgroundQuery property

代码:

Private Sub sht_sub_Refresh_AllConnections_dev()
    'Name: sht_sub_Refresh_AllConnections_dev
    'Purpose: An attempt at using VBA to wait for Queries to finish updating before displaying a message.
    'Description: Waits for a hard coded period of time before dislpaying the message box.
    'State: WIP.
    'Dev: Needs a way to look at the connection stream to somehow detect when its finished.
    
    'DECLARATIONS:
    '------------'
    Dim procName As String              'Stores this procedure's name.
    Dim qTblLst As QueryTables          'A query table collection object.
    Dim qTblObj As QueryTable           'A query table object.
    Dim conLst As Connections           'A connection collection object.
    Dim conObj As WorkbookConnection    'A connection object.
    Dim idx As Long                     'A loop counter.

    'INITIALIZATIONS:
    '---------------'
    procName = "sht_sub_Refresh_AllConnections_dev"    'Store this procedure's name.
    Linit.ini_Setup_Project                            'Setup the project if needed.
    Set conLst = ThisWorkbook.Connections              'Set the connections list object.
    Set conObj = conLst.Item(conLst.Count)             'Set an initial connection object.
    idx = 0                                            'As an exit if the do loop continues without end.
    
    'MAIN CODE BODY:
    '--------------'
    'Turn off backgroundquery for each connection type.
    For Each conObj In conLst                           'For each connection object,
        With conObj
            Select Case .Type                               'Check the connection type,
            Case 1                                        'If its an OLEDB connection then,
                .OLEDBConnection.BackgroundQuery = False    'Set it's backgroundquery property to false.
            Case 2                                        'If its an ODBC connection the,
                .ODBCConnection.BackgroundQuery = False     'Set it's backgroundquery property to false.
            End Select
        End With
    Next conObj
    
    ThisWorkbook.RefreshAll                             'Refresh all connections.
    
    'DEV: Using loops, DoEvents and a query name starting with the letters "zzzz" as suggsted here:
    'https://social.technet.microsoft.com/Forums/en-US/bc3f7748-8a52-498d-951c-4566b8adf45a/in-excel-2016-power-queries-dont-refresh-in-the-background-anymore?forum=powerquery
    'and here:
    'https://www.myonlinetraininghub.com/excel-forum/vba-macros/pause-macro-until-power-queries-finished-refreshing
    'Attempt to wait until the last connection has finished refreshing.
    Do Until Linit.gvTbl_ZZZZZ.QueryTable.Refreshing = True   'Wait until the last table starts refreshing,
        idx = idx + 1                                           'Icrement a loop count,
        If idx > 3000 Then Exit Do                              'If the loop goes longer then 3000 iterations exit,
    Loop                                                      'otherwise continue waiting.
    VBA.DoEvents                                              'Do events before continueing (doens't work).
    Do Until Linit.gvTbl_ZZZZZ.QueryTable.Refreshing = False  'Wait until the last table finishes refreshing,
        idx = idx + 1                                           'Icrement a loop count,
        If idx > 3000 Then Exit Do                              'If the loop goes longer then 3000 iterations exit,
    Loop                                                      'otherwise continue waiting.
    VBA.DoEvents                                              'Do events before continueing (doens't work).
    'DEV: The following is an attempt to get connections to
    '     finish refreshing before code continues as suggested here:
    'https://stackoverflow.com/questions/22083668/wait-until-activeworkbook-refreshall-finishes-vba
    Application.CalculateUntilAsyncQueriesDone         'This is placed here as well as after the refresh.
    VBA.DoEvents                                              'Do events before continueing (doens't work).
    Application.EnableEvents = False                          'Maybe turning off events helps? (nope...),
    Application.ScreenUpdating = False 'This is reset in the procedure called as an argument to the next line:
    Application.OnTime DateAdd("s", 3, Now), _
                       "Lwksh.sht_sub_Msg_RefreshDone"        'The called procedure just displays a message box.
    Application.EnableEvents = True                           'Restore events,
    Application.ScreenUpdating = True                         'Restore screen updating.
    
    'MEMORY CLEANUP:
    '--------------'
EXIT_CLEAN:
    procName = Empty                                     
    Set qTblLst = Nothing
    Set qTblObj = Nothing
    Set conLst = Nothing
    Set conObj = Nothing
    idx = 0
End Sub

代码注释:

  • 代码中以“Linit”开头的任何内容。是通过代码的“INITIALIZATIONS:”部分中的“Linit.ini_Setup_Project”过程调用在过程外部全局设置的对象或变量。
  • 例如,“Linit.gvTbl_ZZZZZ”是一个对象变量,它指向一个空的单行表,该表的名称以字符“zzzz”开头,由 Power Query 生成并加载到 Excel 工作表。该代码显示了网站的链接,在该网站上提出了使用这样的空表的建议。

问题:

  1. 这是否是由于 Power Query 没有内置回调来让 Excel 知道它已完成更新所有刷新过程而造成的失败?
  2. 如果这不是丢失的原因,是否有任何其他方式(此处未描述)可用于在连接尚未完成刷新时以某种方式触发错误,或者在连接尚未完成刷新时触发错误连接完成? (这里的想法是,如果错误不会阻止查询完成,则可以捕获此错误作为检测天气或刷新是否已完成的可能方式。
  3. 有没有办法直接使用 VBA 探测连接流以查找连接关闭或完成状态?
  4. 是否有任何方法可以通过调用 Excel 之外用其他语言(例如 C# 或 Python)编写的程序来直接访问刷新过程?
  5. 您能想到其他可以尝试或测试的方法来实现这项工作吗?我会继续自己寻找答案,但经过一整年的寻找,我感觉有点运气不好。

最佳答案

我理解你的痛苦@neurojelly。我去过那里。但事实证明,解决方案非常简单,并且不使用 VBA。 在“查询属性”窗口中,您需要取消“启用后台刷新”,然后使用DoEvents。 我确信这是有效的,因为我已经使用这种方法一年多了。

请找到包含代码的示例文件的链接。
https://drive.google.com/uc?export=download&id=1ZLxSMEXPLda3QhaQoTyGGv3_sC-tpN-X

Disable background refresh

对于你的第二个问题,可以使用 Iferror/OnEror 方法来检测查询是否返回错误,但它不一定检查查询中的错误。它标识查询本身是否返回错误弹出窗口,运行 VBA 代码时默认情况下会跳过该错误弹出窗口。此方法在大多数情况下有效,但并非总是有效。

关于excel - 如何等待 Power Query 刷新完成?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48000659/

相关文章:

excel - 我如何改进我的功能来处理 Application.FileSearch VBA 的替代方案

R:将工作表附加到 Excel 工作簿,而不阅读整个工作簿

ms-access - 在错误处理中 Access close Recordset

java - Android:语言更改后刷新当前 fragment

python刷新/重新加载

excel - VBA : Error handling only works once in loop

vba - 使用 VBA 将 XLSX 转换为 CSV

vba - VBA : Run-Time error '91' (Code working in template but not “new from template” )

vba - 获取对 VBA 事件处理程序中的“表单”复选框的引用

java - 如何在用户刷新时更新 jsf 列表