かおてっく@Kao_Techさんのツイートで、ThisWorkbook.RefleshAllをすると(「バックグラウンドで更新」状態のクエリがある場合には)リフレッシュが完了する前にコードが先に進んでしまうという話題が出ていて、W.D.@WD4096さんが対策するコード例を出してくださったので、実際に試してみました。
テストコード中の
TargetTable.DataBodyRange.Rows.Delete
のような処理は、テーブルデータの変化がわかりやすいように検証用に入れてあるだけで、実際には不要です(ついでにいうとデータ行が存在しないとエラーが発生するので注意)
- CQtEvents: queryの終了を待つための共通クラスモジュール
- Mod_Test1: QueryTable.Refresh()でBackgroundQuery:=Falseを指定することにより完了を待つパターン
- Mod_Test2: CQtEventsのRefreshedプロパティをDo Loopで監視するパターン
- Mod_Test3: CQtEventsでAfterRefreshイベント発生時に指定した標準プロシージャを実行するパターン
- Mod_Test3All: CQtEventsでAfterRefreshイベント発生時に指定した標準プロシージャを実行するパターン(ブック内の全テーブル対象)
- Mod_TestAll: QueryTable.Refresh()でBackgroundQuery:=Falseを指定することにより完了を待つパターン(ブック内の全テーブル対象)
- Mod_TestConnections: ThisWorkbook.Connectionsを各々Refreshするパターン
- Mod_TestConnections2: ThisWorkbook.Connectionsを各々Refreshするパターン(BackgroundQuery=False設定版)
- Mod_TestConnections3: ThisWorkbook.Connectionsを各々Refreshするパターン(OLEDBConnection.BackgroundQuery=False設定版)
Microsoft 365のExcel(32bit)とExcel 2010(32bit)+Power Query for Excelで、20000レコード強のCSVファイルをPower Queryで読み込んだテーブルにて試してみましたが、共に
- Test1(TestAll)とTest3(Test3All)は期待通りに動作する
- Test2は戻ってこない(Do Loopから抜けない)
- TestConnectionsだと更新完了が待てない(WorkbookConnection.Refreshにはオプションがなく、QueryTable.BackgroundQueryの状態依存)
→QueryTable.BackgroundQueryの状態を保存して一旦Falseにしたあとで更新すれば大丈夫そう(TestConnections2)
→(テーブルをいじる必要がなければ)OLEDBConnection.BackgroundQueryの状態保存→Falseに設定→Refresh→保存した値を戻すの方がシンプル(TestConnections3) - 期待通りに動作したTest1とTest3では、クエリ完了時にテーブルも全て更新されている模様(→(「取り出し中」を監視して)テーブルの更新を待つ必要は、特にないのではないか?)