web-dev-qa-db-fra.com

Comment automatiser une requête de puissance dans VBA?

J'ai des données dans la feuille 1. Normalement, je vais dans Power query et fais mes transformations, puis ferme et charge dans une feuille existante 2.

Je souhaite automatiser cette opération à l'aide de VBA, où je peux simplement exécuter ma requête d'alimentation automatiquement et renseigner la transformation en feuille 2.

L'enregistreur de macros ne semble pas me permettre d'enregistrer les étapes. Et il n'y a pas beaucoup de choses en ligne à faire à ce sujet.

Essayer un code plus simple:

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

Voici mon problème lorsque vous essayez de charger manuellement une nouvelle feuille.

enter image description here

13
JonnyBoy

WIP:

Alors, comment écrire cela pour être suffisant? En bout de ligne, vous devez configurer votre requête à l'aide des outils intégrés, et non de VBA. Vous chargez vos données via la méthode appropriée (fichier, boucle de fichiers dans un dossier, Web, base de données, etc.). La liste s’allonge. Vous pouvez importer depuis des sources externes et charger depuis interne. Jetez un oeil ici pour plus d'informations sur le chargement à partir de sources externes.

Une fois que vous avez sécurisé votre source et que celle-ci est chargée, l'éditeur de requêtes vous permettra de réaliser votre transformation steps .

Le fait est que lorsque vous effectuez vos étapes à l'aide de l'interface utilisateur, le code M est écrit en arrière-plan et constitue la base d'une requête réutilisable, à condition de ne pas modifier le format ou l'emplacement de la source.

Dans votre cas, lorsque vous avez effectué vos étapes et que vous avez une requête comme vous le souhaitez, vous fermez et chargez dans sheet2.

A cette étape, lors de la première configuration, vous sélectionnerez la feuille 2 comme destination de fermeture et de chargement:

Close and load

NB: Lorsque vous sélectionnez une feuille existante, assurez-vous que la feuille 2 existe déjà et que vous pouvez modifier manuellement la feuille Sheet2! devant la plage suggérée.


Vous rencontrez des problèmes parce que vous essayez de recréer tout cela avec du code.

Ne pas Configurez-le à l'aide de l'interface utilisateur et chargez-le dans sheet2. A partir de là, ouvrez l'éditeur de requête pour modifier les étapes et/ou actualisez la requête pour charger la feuille2 existante avec de nouvelles données/actualisées.


Certaines des méthodes disponibles pour actualiser votre requête:

La requête sera actualisée par VBA/Actualisation manuelle de la feuille dans laquelle elle réside (Feuille2), ou du classeur lui-même, par ex. Sheet2.Calculate, ThisWorkbook.RefreshAll, en appuyant manuellement sur le bouton d'actualisation du classeur dans l'onglet Données (ce sont toutes vraiment excessives)

Refresh all tab

Méthodes plus ciblées:

VBA pour la table de requête de la feuille 2:

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

Changez ce qui précède pour le tableau approprié, etc.

Cliquez avec le bouton droit de la souris sur la table de requête et sélectionnez Actualiser:

Refresh

Cliquez sur le bouton d'actualisation dans la fenêtre de requêtes du classeur à droite de la requête en question (icône avec des flèches vertes)

Refresh


Le Ken Pulls moyen VBA (édition mineure de moi)

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

Vous ne devriez pas vraiment avoir besoin de faire tout ce travail via VBA. Vous pouvez avoir certaines manipulations de données délicates que vous vous sentez plus à l'aise avec VBA, puis un accès PowerQuery qui traite les données comme source. Vous pouvez déclencher la totalité du lot en ayant un sous-programme qui appelle la routine de traitement puis utilise l'une des méthodes de commande VBA répertoriées ci-dessus. Il y a plus de méthodes et je les ajouterai quand j'ai plus de temps.


Calculs:

Si vos calculs dépendent de la sortie PowerQuery, vous avez 4 options immédiates évidentes:

  1. Ajoutez ces calculs dans la mesure du possible dans PowerQuery. Il prend en charge les colonnes calculées, les fonctions définies par l'utilisateur et bien plus encore.
  2. Ajoutez la sortie PowerQuery au modèle de données et utilisez-le pour effectuer des calculs, y compris des champs calculés. Cela vous donnera également accès aux fonctions de renseignement sur le temps.
  3. Utilisez VBA pour ajouter les calculs aux zones appropriées de la feuille 2 si la plage change lors de l'actualisation.
  4. Si la plage ne change pas lors de l'actualisation, éloignez simplement vos formules.
11
QHarr

Je suis en désaccord avec la prémisse de la réponse ci-dessus; VBA convient parfaitement à l’automatisation de PowerQuery et est particulièrement efficace pour les travaux répétitifs. L'astuce consiste à créer d'abord la requête dont vous avez besoin dans PowerQuery, puis à utiliser l'éditeur avancé pour capturer le M.. Copiez-le et stockez-le, soit dans une cellule du classeur, soit dans un fichier texte séparé.

La méthode est décrite en détail par Gil Raviv. Pour plus de commodité, je stocke mon M dans des fichiers texte au lieu du classeur et le charge avec:

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

L'avantage des fichiers texte est qu'ils sont indépendants d'Excel et peuvent être réutilisés par de nombreux classeurs.

Voici quelques 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"

Une fois chargé dans VBA (à partir d'un classeur ou d'un fichier texte), le M peut être modifié dans VBA, par exemple en substituant des mots de substitution, ou en utilisant les noms de commande M pour localiser et modifier les lignes si nécessaire, par exemple.

    ' 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 & """")

L'étape suivante consiste à charger la requête. Je le fais en utilisant le technique décrite par Gil comme suit:

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 

Le code de Gil permet d'importer des données dans le modèle de données ou dans une feuille de calcul. Le PO nécessite le second et si la méthode est suivie, les données transformées doivent apparaître dans la feuille de calcul.

7
mer_curius