如何在 VBA 中自动执行电源查询?

Posted

技术标签:

【中文标题】如何在 VBA 中自动执行电源查询?【英文标题】:How to automate a power query in VBA? 【发布时间】:2018-07-17 16:48:37 【问题描述】:

我在工作表 1 中有数据。通常我会进行电源查询并进行转换,然后关闭并加载到现有工作表 2。

我想使用 VBA 自动执行此操作,我可以在其中自动运行我的电源查询并将转换填充到工作表 2。

宏记录器似乎不允许我记录步骤。而且网上没有太多关于这样做的内容。

尝试一些更简单的代码:

Sub LoadToWorksheetOnly()

'Sub LoadToWorksheetOnly(query As WorkbookQuery, currentSheet As Worksheet)
    ' The usual VBA code to create ListObject with a Query Table
    ' The interface is not new, but looks how simple is the conneciton string of Power Query:
    ' "OLEDB;Provider=Microsoft.Mashup.OleDb.1;Data Source=$Workbook$;Location=" & query.Name
     
    query = Sheets("Sheet6").Range("A1").value 'here is where my query from power query is. I put the text from power query avanced editor in another sheet cell.
    currentSheet = ActiveSheet.Name
    With ActiveSheet.ListObjects.Add(SourceType:=0, Source:= _
        "OLEDB;Provider=Microsoft.Mashup.OleDb.1;Data Source=$Workbook$;Location=" & query.Name _
        , Destination:=Sheets("target").Range("$A$1")).QueryTable
        .CommandType = xlCmdDefault
        .CommandText = Array("SELECT * FROM [" & query.Name & "]")
        .RowNumbers = False
        .FillAdjacentFormulas = False
        .PreserveFormatting = True
        .RefreshOnFileOpen = False
        .BackgroundQuery = True
        .RefreshStyle = xlInsertDeleteCells
        .SavePassword = False
        .SaveData = True
        .AdjustColumnWidth = True
        .RefreshPeriod = 0
        .PreserveColumnInfo = False
        .Refresh BackgroundQuery:=False
    End With
     
End Sub

这是我尝试手动加载到新工作表时遇到的问题。

【问题讨论】:

我设置了查询,加载到工作表 2。我不知道如何重复此操作一次。第二次它不会让我加载到工作表 2。我认为有一个代码来为我做这件事会很方便。 是的。我已经更新了我的代码,我想在表 1 上进行查询,并使用 vba 在表 2 中显示结果,请查看新代码。 @Qharr, 1) 这是我想做的正确逻辑,刷新然后加载到工作表 2,但是我无法将其加载到工作表 2。我右键单击显示窗格中的查询,然后加载到,然后是表。但是应该加载数据的地方不允许我点击(见上图)。 2) 是否有 vba 代码自动刷新并加载到工作表 2? @QHarr 谢谢。你知道是否有自动更新的方法吗?不一定是vba? @QHarr,我正在从 excel 表中进行内部查询,将两张表合二为一。 (表 1) 【参考方案1】:

VBA 绝对适合自动化 PowerQuery,并且对于重复性工作特别有效。诀窍是首先在 PowerQuery 中创建您需要的查询,然后使用高级编辑器捕获 M。将其复制并存储在工作簿的单元格中或单独的文本文件中。

method 由 Gil Raviv 详细描述。为方便起见,我将 M 存储在文本文件而不是工作簿中,并使用以下命令加载:

Function LoadTextFile(FullFileName As String) As String
  With CreateObject("Scripting.FileSystemObject")
    LoadTextFile = .OpenTextFile(FullFileName, 1).readall
  End With 
End Function

文本文件的好处是它们独立于 excel 并且可以被许多工作簿重复使用。

这是一些M:

let
// load the reference file (variables are shown in capitals;  
// variable values are replaced with strings from the excel control workbook)
    Source = Excel.Workbook(File.Contents(PATH_AND_NAME), null, true),
    ImportSheet = Source[Item=SHEET_NAME,Kind="Sheet"][Data],
    #"Promoted Headers" = Table.PromoteHeaders(ImportSheet),
    #"Changed Type" = Table.TransformColumnTypes(#"Promoted Headers","ACCOUNT", type text)
in
    #"Changed Type"

一旦加载到 VBA(从工作簿或文本文件),M 就可以在 VBA 中进行编辑,例如通过替换占位符词,或在必要时使用 M 命令名称来定位和更改行,例如

    ' create the M script to read the M file that will do the import
        M_Script = LoadTextFile(M_Source)
        
    ' insert the path
        M_Script = Replace(M_Script, "PATH_AND_NAME", """" & qSource & """") 
    
    ' insert the worksheet name
        If wksName <> "" Then M_Script = Replace(M_Script, "SHEET_NAME", """" & wksName & """")
        

下一步是加载查询。我使用technique described by Gil 执行此操作,如下所示:

Dim qry As WorkbookQuery

If DoesQueryExist(qName) Then 
    ' Deleting the query 
    Set qry = ThisWorkbook.Queries(qName) 
    qry.Delete 
End If 
           
Set qry = w.queries.Add(qName, M_Script, qSource)
                      
' We check if data should be loaded to Data Model 
shouldLoadToDataModel = ThisWorkbook.Worksheets(1).Cells(13, "D") 
 
' We check if data should be loaded to worksheet 
shouldLoadToWorksheet = ThisWorkbook.Worksheets(1).Cells(13, "E") 
 
If shouldLoadToWorksheet Then 
    ' We add a new worksheet with the same name as the Power Query query 
    Set currentSheet = Sheets.Add(After:=ActiveSheet) 
    currentSheet.Name = qName 
 
    If Not shouldLoadToDataModel Then 
        ' Let's load to worksheet only 
        LoadToWorksheetOnly qry, currentSheet 
    Else 
        ' Let's load to worksheet and Data Model 
        LoadToWorksheetAndModel qry, currentSheet 
    End If 
ElseIf shouldLoadToDataModel Then 
    ' No need to load to worksheet, only Data Model 
    LoadToDataModel qry 
End If 

这是 LoadToDataModel 函数:

Option Explicit
Function LoadToDataModel(w As Workbook, query As WorkbookQuery, error As Integer) As Boolean
On Error GoTo Load_Error
    ' This code loads the query to the Data Model
    w.Connections.Add2 "Query - " & query.Name, _
        "Connection to the '" & query.Name & "' query in the workbook.", _
        "OLEDB;Provider=Microsoft.Mashup.OleDb.1;Data Source=$Workbook$;Location=" & query.Name _
        , """" & query.Name & """", 6, True, False

    LoadToDataModel = True
    
Load_Exit:
    Exit Function
    
Load_Error:
    LoadToDataModel = False
    error = Err.Number
    Resume Load_Exit
End Function

还有 LoadToWorksheetOnly 的功能(感谢 wayback machine):

Sub LoadToWorksheetOnly(query As WorkbookQuery, currentSheet As Worksheet) 
    ' The usual VBA code to create ListObject with a Query Table 
    ' The interface is not new, but looks how simple is the conneciton string of Power Query: 
    ' "OLEDB;Provider=Microsoft.Mashup.OleDb.1;Data Source=$Workbook$;Location=" & query.Name 
     
    With currentSheet.ListObjects.Add(SourceType:=0, Source:= _ 
        "OLEDB;Provider=Microsoft.Mashup.OleDb.1;Data Source=$Workbook$;Location=" & query.Name _ 
        , Destination:=Range("$A$1")).QueryTable 
        .CommandType = xlCmdDefault 
        .CommandText = Array("SELECT * FROM [" & query.Name & "]") 
        .RowNumbers = False 
        .FillAdjacentFormulas = False 
        .PreserveFormatting = True 
        .RefreshOnFileOpen = False 
        .BackgroundQuery = True 
        .RefreshStyle = xlInsertDeleteCells 
        .SavePassword = False 
        .SaveData = True 
        .AdjustColumnWidth = True 
        .RefreshPeriod = 0 
        .PreserveColumnInfo = False 
        .Refresh BackgroundQuery:=False 
    End With 
     
End Sub 

Gil 的代码允许将数据导入数据模型或工作表。 OP需要第二个,如果按照方法,转换后的数据应该出现在工作表中。

【讨论】:

当工作要分发给具有不同 Excel 数据操作技能水平的广泛受众时,此答案中倡导的理念最为相关。设置自动化数据访问方法可以利用远程数据访问和转换的好处,而无需您的整个员工掌握广泛的 UI 数据菜单的细节。许多人在遇到抽象的数据时会遇到困难。 “Gil Raviv 详细描述了该方法”但该链接不再有效。您是否有机会将缺少的子项(LoadToWorksheetOnly、LoadToDataModel)添加到上面的代码中? 添加了来自web.archive.org/web/20200318054225/https://…的缺失子【参考方案2】:

使用内置工具设置查询会更容易,而不是 VBA。 不是您不能这样做,因为似乎至少有一个其他回答者误解了我的观点,使用内置的优化工具更容易,而且更改速度比不断地将您的数据复制到其他地方,转义“”,追溯 M 语言错误等。然后您可以通过 VBA 运行该查询。

您可以通过适当的方法加载数据,这些方法可以是从文件、文件夹中的循环文件、Web、数据库……等等。您可以从外部源导入,也可以从内部加载。查看here 了解有关从外部源加载的更多信息。

一旦您保护了您的源并加载了它,您将看到查询编辑器,您可以在其中执行转换steps。

关键在于,当您使用 UI 执行步骤时,M 代码会在后台编写,并构成可重用查询的基础,前提是您不更改源格式或位置。如果你这样做了,那么在 UI 中更新适当的数据源是一个简单的编辑。

在您的情况下,当您执行了您的步骤并有您希望的查询时,然后关闭并加载到 sheet2。

在这一步,第一次设置时,您将选择表 2 作为关闭和加载目标:

注意:当您选择现有工作表时,请确保工作表 2 已经存在,您可以手动编辑工作表 2!在建议范围之前。


您遇到问题是因为您一直在尝试使用代码重新创建所有这些内容。

不要。使用 UI 进行设置并加载到 sheet2。从那时起,打开查询编辑器以编辑步骤和/或刷新查询以使用新的/刷新的数据加载现有的 sheet2。


刷新查询的一些可用方法:

查询将通过 VBA/手动刷新到它所在的工作表 (Sheet2) 或工作簿本身,例如Sheet2.CalculateThisWorkbook.RefreshAll,手动按下数据选项卡中的刷新工作簿按钮(这些都是大材小用)

更有针对性的方法:

工作表 2 中查询表的 VBA:

ThisWorkbook.Worksheets("Sheet2").ListObjects(1).QueryTable.Refresh BackgroundQuery:=False   

将以上内容更改为适当的表格等。

右键单击查询表本身并选择刷新:

单击右侧工作簿查询窗口中相关查询的刷新按钮(带有绿色圆圈箭头的图标)


Ken Pulls VBA 方式(我稍作修改)

Option Explicit
Public Sub UpdatePowerQueries()
    ' Macro to update my Power Query script(s)

    Dim lTest As Long, cn As WorkbookConnection
    On Error Resume Next
    For Each cn In ThisWorkbook.Connections
        lTest = InStr(1, cn.OLEDBConnection.Connection, "Provider=Microsoft.Mashup.OleDb.1", vbTextCompare)
        If Err.Number <> 0 Then
            Err.Clear
            Exit For
        End If
        If lTest > 0 Then cn.Refresh
    Next cn
    On Error GoTo 0
End Sub

您应该没有任何实际需要通过 VBA 完成所有这些工作。您可能有一些棘手的数据操作,您觉得使用 VBA 做起来更舒服,然后让 PowerQuery 访问已处理的数据作为源。您可以通过有一个调用处理例程然后使用上面列出的 VBA 命令方法之一的子例程来启动整个批次。方法还有很多,等有时间再补充。


计算:

如果您的计算依赖于 PowerQuery 输出,您有 4 个明显的直接选项:

    尽可能将这些计算添加到 PowerQuery 中。它支持计算列、用户定义的函数等等。 将 PowerQuery 输出添加到数据模型并使用数据模型执行计算,包括计算字段。这也可以让您访问时间智能功能。 如果刷新时范围发生变化,请使用 VBA 将计算添加到工作表 2 中的相应区域 如果刷新时范围没有变化,只需将公式移开即可。

【讨论】:

【参考方案3】:

这次谈话有点晚了,但我发现通过编辑公式属性在 VBA 中更新 Power Query 相当容易。

首先更新let..in 公式。然后刷新使用它的连接

Dim pqFormula as String
pqFormula = "let..in"

Dim pqName as String
pqName = "<Name of the connection>"

' Update the formula for the specific power query.
ThisWorkbook.queries(pqName).Formula = pqFormula

' Refresh the connection
ThisWorkbook.Connections("Query - " & pqName).Refresh

基于该连接的所有表都应立即更新。

问候,

【讨论】:

以上是关于如何在 VBA 中自动执行电源查询?的主要内容,如果未能解决你的问题,请参考以下文章

EXCEL如何让VBA程序自动切换下一个工作表

Deepin电源管理和开机自动执行指令进入省电模式(CPUPOWER)

如何自动填充公式 VBA

win10如何设置通电自动开机

VBA:运行时自动化错误 - “代码执行已被中断”

如何在电源查询中执行 COALESCE?