web-dev-qa-db-ja.com

Power Queryの更新が完了するのを待つ方法は? 「BackgroundQuery = False」で「DoEvents」を試しましたが、何も機能しません

セットアップ:

  • Windows 7(作業中)
  • Windows 10(自宅)
  • Excel 2016(動作中のビルド4627)
  • Excel 2016(自宅で8730をビルド)
  • Power Queryは、Excelファイルのフォルダーをインポート、追加、および変換するように設定されています。このステップは機能します。

問題の説明

以下に示す「試したこと:」セクションで説明されているように、任意の手法を任意に組み合わせて使用​​し、PowerQueryが更新を完了するのを待った後。 [クエリと接続]ペインに表示される更新インジケーター(スピナー?)アイコンに従って、Power Queryテーブルの更新が完了する前に、メッセージボックスを表示し、その他のコードを実行できます。

上記のステートメントの例外は、以下の「コード」セクションに示されているApplicationクラスの「OnTime」メソッドです。これは、電源クエリの更新のポーリングを中断するようには見えません。ただし、この方法の問題は、ハードコードされた時間を使用してVBAコードを一時停止することです。これは、クエリされるデータのサイズ、量、および期間が時間とともに変化するため、常に機能するとは限りません。

私が試したこと:

  • 「DoEvents」、「BackgrgoundQuery = False」、CalculateUntilAsyncQueriesDoneのメソッドとプロパティの使用について説明しているすべてのStackOverflow(およびその他のWebサイトリソース)を読みました。
  • このリンクで提案されているように、 クエリ更新イベントの前後に作成 のクラスを作成しようとしました(以下のコード例には示されていません)。
  • QueryTableメソッドの.Refreshing = True/Falseプロパティを指定して、Do until/Whileループを使用して、更新が終了するのを待ちました。
  • ここで「subro」によって提案されているように、Excelメニュー(メニューバー->データ->接続->プロパティ)のBackgroundQueryプロパティをFalseに設定しようとしました: ActiveWorkbook.RefreshAllが終了するまで待つ-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」という文字で始まる名前を持つ空の1行のテーブルを指すオブジェクト変数であり、Power Queryによって生成され、Excelシートに読み込まれます。このコードは、このような空のテーブルを使用するための提案が行われたWebサイトへのリンクを示しています。

質問

  1. これは、Power Queryに、更新プロセスの更新が完了したことをExcelに通知するためのコールバックが組み込まれていないために失われた原因ですか?
  2. これが失われた原因ではない場合、接続の更新がまだ完了していない場合に何らかの方法でエラーをトリガーしたり、接続が終了したときにエラーをトリガーしたりするために使用できる、ここで説明されていない他の方法はありますか? ? (ここでの考え方は、このエラーが天気を検出するための可能な方法としてトラップされる可能性があるか、エラーがクエリの終了を停止しない限り、更新が完了していない可能性があるということです)。
  3. VBAを使用して接続ストリームを直接プローブし、接続が閉じているか終了した状態を探す方法はありますか?
  4. C#やPythonなどの他の言語で書かれたExcelの外部のプログラムを呼び出すことで、更新プロセスに直接アクセスする方法はありますか?
  5. これを機能させるために試行またはテストされる可能性のある他の何かについて考えられますか?自分で答えを探し続けますが、一年かけて探してみたら少し運が悪かったです。

この問題にご関心をお寄せいただき、ありがとうございます。情報を含めるのを忘れた場合、またはこの質問をより明確にするために何かを言い換える必要がある場合は、コメントで知らせてください。

8
neurojelly

@neurojellyの痛みを理解しました。私はそこに行ったことがあります。しかし、結局のところ、解決策は非常に単純であり、VBAを使用していません。 [クエリのプロパティ]ウィンドウで、[バックグラウンド更新を有効にする]のチェックを外してチェックを外しDoEventsを使用する必要があります。私はこの方法を1年以上使用しているので、これが機能することは確かです。

コードが含まれているサンプルファイルへのリンクを見つけてください。
https://drive.google.com/open?id=1ZLxSMEXPLda3QhaQoTyGGv3_sC-tpN-X
Disable background refresh


2番目の質問については、Iferror/OnErorメソッドを使用して、クエリがエラーを返すかどうかを検出できますが、必ずしもクエリのエラーをチェックするわけではありません。クエリ自体がエラーポップアップを返しているかどうかを識別します。エラーポップアップは、VBAコードの実行中にデフォルトでスキップされます。この方法はほとんどの場合機能しますが、常に機能するとは限りません。

1
Gangula

これが回避策です

Sub MyProcedure()

    '
    ' Some procedures
    '
    Call ActiveWorkbook.RefreshAll
    Call NotifyWhenRefreshComplete
End Sub



Private Sub NotifyWhenRefreshComplete()
    Const PulseTimer As Currency = TimeValue("00:00:01")
    Dim b1 As Boolean, b2 As Boolean

    b1 = Sheet1.Range("ListObject1").ListObject.QueryTable.Refreshing
    b2 = Sheet1.Range("ListObject2").ListObject.QueryTable.Refreshing

    If b1 Or b2 Then
        Call Application.OnTime(Now + PulseTimer, "NotifyWhenRefreshComplete")
    Else
        Call MsgBox("Refresh Complete.", vbOKOnly)
    End If
End Sub

ListObject1とListObject2は公開されたテーブルです。更新が完了したかどうかを確認するには、公開されたテーブルのみが必要です。未公開のテーブルを確認する必要はありません。

ただし、公開されたテーブルが多数ある場合は、すべてのActiveWorkbook.connectionsを繰り返し処理し、すべてのwbConn.OLEDBConnection.Refreshingステータスがfalse状態に戻っているかどうかを確認し、b1およびb2ブール値を置き換えることは間違いありません。

注:何らかの理由で、DoEventsの使用を拒否します。接続が実行されている間もユーザーがExcelで作業を続行できるようにし、更新が完了したらメッセージを表示するようにします。ただし、上記のようにOnTime呼び出し元ではなく、Do... Loop反復を使用して実装する場合は、先に進んでください。

最後に、Public WithEvents qt As QueryTableの下にコールバックがあり、qt.refreshingを探します。この方法も使用できます。

お役に立てれば。

1
Rosetta