С выходом Power Query в массы все чаще стал появляться вопрос: как обновить запросы один за другим в заданном порядке?
В книге может быть десять и более запросов. И только 4 из них надо обновлять постоянно, да еще и в заданном порядке. Например, у нас есть с десяток запросов по продажам, отгрузкам и прочей аналитике, но только 4 из них нам надо обновить. При этом обновить надо в строгой последовательности:
- Вручную
- Кнопка Обновить все(Refresh All)
- Написать собственный порядок обновления через Visual Basic for Applications
Можно обновить запросы стандартными методами, поочередно вручную обновляя каждый запрос. Но это не всегда удобно, особенно если запросов более 5. Значит этот вариант не разбираем в деталях в принципе - все его плюсы и минусы очевидны.
Кнопка Данные
На самом деле, если мы имеем дело с Power Query, то правильнее всего выстраивать все взаимосвязи уже на стадии создания этих запросов в любом случае. Т.е. создавать их в правильной последовательности и "затягивать" данные в основной запрос в самую последнюю очередь, ориентируясь на результаты вспомогательных запросов. Это оптимальный вариант, т.к. в этом случае достаточно будет просто обновить нужный запрос, а Power Query сама определит какие вспомогательные запросы необходимо обновить для корректного результата. При этом встроенный механизм Power Query достаточно хорошо справляется с кешированием данных и даже если обновление происходит через кнопку Обновить все
Но здесь у нас опять же возникает проблема - если нам не надо обновлять все запросы в книге - как Power Query узнает, какие из них надо обновлять, а какие нет? Ответ - никак. Поэтому хоть и следует всегда стараться выстраивать цепочки взаимодействий запросов в "правильном" порядке - нашу проблему с обновлением только части запросов это никак не решит. В этом случае можно создать мастер запрос, в который просто через объединение запросов собирать все нужные. И обновление этого запроса должно инициировать обновление всех указанных в нем запросов со всеми их цепочками. Как создать подобный мастер запрос:
- идем на вкладку
Главная -Объединить -Добавить запросы :
- в появившемся окне выбираем "Три таблицы и больше"
- добавляем все нужные запросы
- нажимаем Ок
Все, запрос готов. Теперь достаточно обновить только его, чтобы все связанные запросы обновились. Правда, есть одна проблема: если данных много, то такой запрос их увеличит вдвое - ведь мы в один запрос собираем все остальные...
В решении через мастер-запрос есть одна проблема - запросы могут быть не только Power Query, но и более старых версий. Здесь вариантов вообще не остается - только Visual Basic for Applications. Да и в случае, если запросы уже созданы ранее кем-то еще или навыков создать мастер-запрос нет.
'--------------------------------------------------------------------------------------- ' Author : Щербаков Дмитрий(The_Prist) ' Профессиональная разработка приложений для MS Office любой сложности ' Проведение тренингов по MS Excel ' https://www.excel-vba.ru ' info@excel-vba.ru ' Purpose: Код обновляет поочередно указанные запросы в заданном порядке '--------------------------------------------------------------------------------------- Sub RefreshConnections() Dim oc, xQuery, aConnections, xConName Dim IsBG_Refresh As Boolean 'создаем перечисление всех запросов, которые надо обновить 'располагаем их в том порядке, в котором необходимо обновлять aConnections = Array("Запрос — Отделы", "Запрос — Сотрудники", "Заказы и Продажи", "Запрос — Бюджет") 'перебираем и обновляем все запросы поочередно For Each xConName In aConnections Set oc = ActiveWorkbook.Connections(xConName) Set xQuery = Nothing Select Case oc.Type Case Excel.XlConnectionType.xlConnectionTypeODBC Set xQuery = oc.ODBCConnection Case Excel.XlConnectionType.xlConnectionTypeOLEDB Set xQuery = oc.OLEDBConnection Case Else 'запрос выгружен в таблицу на листе If oc.Ranges.Count > 0 Then Set xQuery = oc.Ranges(1).QueryTable End If End Select 'непосредственно обновление запроса с ожиданием окончания обновления If Not xQuery Is Nothing Then 'запоминаем значение обновления в фоне для запроса IsBG_Refresh = xQuery.BackgroundQuery 'выставляем принудительно ждать завершения запроса xQuery.BackgroundQuery = False 'обновляем запрос xQuery.Refresh 'возвращаем обновление в фоне в первоначальное состояние xQuery.BackgroundQuery = IsBG_Refresh End If Next End Sub |
Чтобы использовать приведенный код для своих запросов достаточно в строке:
aConnections = Array("Запрос — Отделы", "Запрос — Сотрудники", "Заказы и Продажи", "Запрос — Бюджет") |
внутри
Код обновляет не только запросы Power Query, но и запросы более старых версий, выгруженных просто на лист.
Что делать, если
Да, вполне логичная ситуация, когда запросов много и переписывать имя каждого запроса не такая уж простая задача. Очень легко ошибиться в имени и тогда обновления не будет. Поэтому ниже я прилагаю код, который выводит на новый лист имена всех запросов:
'--------------------------------------------------------------------------------------- ' Author : Щербаков Дмитрий(The_Prist) ' Профессиональная разработка приложений для MS Office любой сложности ' Проведение тренингов по MS Excel ' https://www.excel-vba.ru ' info@excel-vba.ru ' Purpose: Код выводит на лист все запросы в текущей книге '--------------------------------------------------------------------------------------- Sub GetAllConnections() Dim ws As Worksheet Dim lr As Long Dim oc, aList() 'создаем массив для запоминания имен всех запросов ReDim aList(1 To ActiveWorkbook.Connections.Count, 1 To 1) For Each oc In ActiveWorkbook.Connections lr = lr + 1 'увеличиваем счетчик aList(lr, 1) = oc.Name Next 'создаем новый лист, в который записываем имена всех запросов Set ws = ActiveWorkbook.Worksheets.Add(before:=ActiveWorkbook.Worksheets(1)) 'оформляем заголовок(для эстетики) ws.Cells(1, 1).Value = "Имя запроса" ws.Cells(1, 1).Font.Bold = True 'записываем имена всех запросов ws.Cells(2, 1).Resize(lr, 1).Value = aList End Sub |
Так же см.:
Как дождаться обновления выполнения запроса
Порядок обновления запросов
Управление обновлениями запросов
Время обновления запросов
Спасибо большое за статью, очень помогла
Подскажите, а есть возможность часть запросов обновлять вместе, а часть по отдельности в одном макросе?
Ирина, если правильно понял вопрос - то возможно поможет следующее: открываете панель запросов(Данные -Запросы и подключения) -правая кнопка мыши на запросе -Свойства. Там есть галочка "Обновить это подключение по команде Обновить все". Для тех запросов, которые надо обновлять вместе - галочку оставляете. Для остальных - снимаете.
В итоге те запросы, которые надо обновить вместе можно обновлять кнопкой "Обновить все", а другие - в заданном порядке.
Только помните - если есть связанные запросы, то даже если галочка не установлена, а от этого запроса зависит результат другого запроса - он будет обновлен.
Здравствуйте! Использовала Ваш макрос для обновления Запросов, но у меня есть один нюанс. Указала все запросы в той последовательности, в которой необходимо их обновлять. Источники данных - разные (внешние). Но есть один запрос, который создан на основании другого запроса из этого файла, который я обновляю макросом. Как мне обновить этот запрос последним. Если я его включаю в массив всех имен запросов - выдает ошибку. Надеюсь, я Вас не запутала. )
Сложно ответить без файлов и без понимания, какая вообще ошибка возникает(текст, номер) и на какой строке кода.
Возможно, имя запроса неверно задано. Возможно, идет ошибка обновления вложенного запроса. В любом случае без конкретики все сводится к гаданию.
Добрый день! Ошибка "Элементов в перечислении было недостаточно для выполнения операции" на строке xQuery.Refresh. И еще возможно немаловажный момент, я работаю в Excel 2013. Запросы вставляла копированием после выполнения макроса Sub GetAllConnections().
Ошибка точно не имеет отношения к кодам, которые представлены в статье. Это ошибка внутри одного из запросов и надо вычислять какого именно.
Проверила Запрос на котором спотыкается, да там имя Листа изменилось. Но ошибка изначально была не в этом, но я ее тоже устранила, она была в том, что у меня был запрос с именем "X", я его задублировала, затем дубль оставила, а первоначальный запрос удалила, имя изменила на "X". А в подключениях как потом выяснилось имя осталось "X(1)". И проблема все-таки не в этом, мне сейчас не хватает времени описать ее, чуть позже...
У меня на офис 365 отрабатывает корректно код. Как я уже ранее сказала, возможно мне проблем добавляет факт, что у меня Excel 2013 на работе. В Excel 2013 при запуске макроса начинают обновляться сразу несколько запросов и затем почти сразу выдается сообщение, что обновление завершено. Но и понятно, что запросы не обновились… Все запросы используют внешние источники. А есть еще один запрос, который я обновляю отдельно после всех, его я делаю так… После выгрузки таблиц в книгу, я создаю запрос и загружаю этот же файл в редактор и беру все таблицы с Лист1. Поэтому я в макросе добавила сохранение книги, а потом уже добавила обновление последнего запроса. Посмотрите, пожалуйста, макрос, а то я могу ведь накрутить...)
Sub RefreshConnections()
' Выводим сообщение с именами обновленных запросов
MsgBox "Запросы обновлены", vbInformation
On Error Resume Next
With Worksheets(1).UsedRange: End With
ThisWorkbook.Save
ThisWorkbook.Connections("Запрос — ХХХ_ХХ(1)").Refresh
MsgBox "Укажите папку для сохранения файла", vbInformation
Call SavePrefix
ActiveWorkbook.Worksheets(1).Activate
End Sub
Sub SavePrefix()
Dim fileName As String
Dim Path As String
Dim Prefix As String
fileName = ThisWorkbook.Name
Prefix = Format(Now, "dd.mm.yyyy_hh-mm_")
1 With Application.FileDialog(msoFileDialogFolderPicker)
If .Show = -1 Then
Path = .SelectedItems(1)
Else
MsgBox "Вы не выбрали путь для сохранения.": GoTo 1
End If
End With
Excel.Application.DisplayAlerts = False
ThisWorkbook.SaveAs fileName:=Path & "\" & Prefix & fileName & ".xlsx", FileFormat:=xlOpenXMLWorkbook
Excel.Application.DisplayAlerts = True
End Sub
Но вот и вся проблема...Что думаете по этому поводу, понимаю, что скорее всего не в коде дело, но как же мне тогда быть? Мне это архиважно именно на работе, потому что коллеги не будут отслеживать что обновилось, а что нет и это мне необходимо автоматизировать. Я так надеялась, что я смогу это сделать и вот такое...((