Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Lesedauer 9 Min.

Power Query: Das bessere Query-Tool

Das leistungsstarke Werkzeug wurde geschaffen, um Daten abzurufen und zu trans­formieren. Im Zusammenspiel mit Visual Studio wird es noch mächtiger.
© Autor
Mit Office 2010 hat Microsoft für Excel mehrere Werkzeuge veröffentlicht, die einen erweiterten Datenbankzugriff und Möglichkeiten eines Reportings eröffnen: Power Query, Power Pivot, Power Map und Power View. Man musste sie in der damaligen Excel-Version aus dem Internet herunterladen und in Excel installieren. Ab der Version 2013 ist Power Query fester Bestandteil von Excel. In der Version 2016 und 2019 residiert dieses Programm in Excel unter Daten abrufen und transformieren im Register Daten.Parallel zu Excel wurde dieses Werkzeug in Power BI integriert, das kostenlos heruntergeladen werden kann [1]. Nicht nur, dass Microsoft damit ein mächtiges Werkzeug für Excel und Power BI geschaffen hat; auch im Zusammenspiel mit Visual Studio wird es zu einer cleveren Hilfe für den Zugriff auf Daten und für deren Verarbeitung.

Abrufen: Verbindung zu externen Quellen

Excel und Power BI können mit Daten abrufen und transformieren – wir sprechen in diesem Artikel von Power Query, dem Namen, unter dem es in Excel 2013 installiert wurde – auf sehr unterschiedliche Datenquellen zugreifen. Dabei sind einige sehr bemerkenswert. Doch der Reihe nach.In Excel können Sie einen Datenbereich als Tabelle formatieren oder ihn in eine Tabelle umwandeln. In der Literatur und in Online-Videos bezeichnen viele diese Liste als „intelligente Tabelle“ oder „formatierte Tabelle“ (Bild 1). Diese kann ebenso in Power Query eingebunden werden wie ein Tabellenblatt einer gespeicherten Excel-Arbeitsmappe, eine Text- oder CSV-Datei, eine XML- oder eine JSON-Datei. Aber auch einen Ordner (mit allen darin befindlichen Excel-Tabellen oder Dateien anderer Formate) kann man als Grundlage für einen Datenzugriff verwenden, ebenso wie eine Internetseite, die Tabellen enthält. Möglich ist der Zugriff auf eine SharePoint-Liste oder eine Azure-Datenbank. Interessant ist sicher auch die immense Zahl weiterer Datenbanken, die Power Query anzapfen kann – von Access bis Oracle und SQL Server, wie man auf Bild 2a und Bild 2b erkennen kann.
Eine intelligente/formatierte ­Tabelle– erkennbar an den ­Tabellentools(Bild 1) © Autor
… und ebenso umfangreichist die Liste der Daten­banken, die Power Query anzapfen kann(Bild 2b) © Autor
Die Liste an Dateiformaten, auf die Power Queryzugreifen kann, ist lang …(Bild 2a) © Autor
Mit einer Einfach- oder Mehrfachselektion können diese Tabellen, Listen oder Abfragen importiert werden. Wird eine Tabelle einer dieser Datenquellen geladen und bearbeitet, so wird sie im Power-Query-Editor geöffnet (Bild 3).
Der Power-Query-Editor(Bild 3) © Autor

Schließen & Laden in

Im Power-Query-Editor kann die Liste – nachdem sie transformiert wurde – mit der Schaltfläche Schließen & Laden in zurück nach Excel geschrieben werden. Dabei stehen Ihnen zwei Varianten zur Verfügung: Entweder Sie schreiben die Daten als Tabelle, PivotTable oder PivotChart in ein vorhandenes oder neues Tabellenblatt. Oder Sie erstellen eine Verbindung. Dann wird diese Verbindung (und alle anderen vorhandenen Verbindungen) im Aufgabenbereich Abfragen und Verbindungen angezeigt, wie Sie in Bild 4 sehen.
Die Datenwerden nach Excel zurückgeschrieben – als Tabelle oder als Verbindung(Bild 4) © Autor

Der Editor

Zurück zum Editor: Auf dessen linker Seite befindet sich der Aufgabenbereich, der die Abfrage beziehungsweise die Abfragen anzeigt. Das Fenster Abfrageeinstellungen auf der rechten Seite listet alle Schritte auf, die getätigt werden. Der aktuelle Schritt wird in der Bearbeitungsleiste gezeigt.Das Tabellenfenster zeigt das Ergebnis der aktuellen Abfrage an – allerdings nicht vollständig, sondern nur die ersten 100 Zeilen. Scrollt man an den unteren Rand, werden wei­tere 100 Zeilen eingeblendet. Das bedeutet: Man kann nicht zum unteren Rand der Liste springen. Andererseits beschleunigt dieses Query-Folding den Zugriff auf die Daten, da hierfür nur die ersten Datensätze der Datenquelle gelesen und angezeigt werden müssen (Bild 5).
Mehrere Abfragenim Editor(Bild 5) © Autor

Anpassen der Daten: Transformation

Im Power-Query-Editor können Sie all die Aktionen durchführen, für die auch Excel Assistenten, Funktionen und Befehle zur Verfügung stellt:
  • Zeilen und Spalten löschen und einfügen,
  • sortieren und filtern,
  • gruppieren,
  • Werte ersetzen,
  • Spalten trennen und zusammenfügen,
  • ausfüllen nach unten.
Das alles wäre – für Excel – noch nichts Ungewöhnliches. Da die Tabelle Datenbankeigenschaften aufweist, können die Feldnamen (hier: Überschriften) umbenannt werden. Aber man kann auch mit einem Klick die Überschrift als erste Zeile verwenden oder, umgekehrt, die erste Zeile als Beschriftung der Spalten verwenden. Transponieren ist selbstredend auch kein Problem. Man kann leicht Spalten zusammenfassen – dies wäre in Excel nur mit mehreren Schritten, beispielsweise mit der Funktion VERKETTEN oder TEXTVERKETTEN und kopieren | Inhalte einfügen als Werte, möglich.Datentypen können und sollten festgelegt werden. Die Datentypen werden als Symbol im Header angezeigt. Datenfeldtypen sind in Excel bekannterweise nicht möglich. Etwas versteckt platziert kann auch über das Kontextmenü das Gebietsschema für Datumswerte und Zahlen festgelegt werden – eine in Excel fehlende Funktionalität, die leider häufig zu Problemen führt. Bild 6 zeigt das Vorgehen.
Die Datentypenkönnen auch über das Kontextmenü festgelegt werden(Bild 6) © Autor

Die angewendeten Schritte

Jeder Schritt, der durchgeführt wurde, wird im Aufgabenbereich Abfrageeinstellungen mitprotokolliert. Dort können Sie zu einem weiter zurückliegenden Schritt navigieren. Fehlerhafte Anweisungen lassen sich löschen. Mehr noch: Sie können sogar zwischen zwei weiter zurückliegenden Schritten einen neuen Schritt einfügen. In der ­Bearbeitungsleiste ist jeder einzelne Schritt einsehbar. Über den erweiterten Editor, den Sie in der Gruppe Abfrage in der Registerkarte Start finden, können Sie sämtliche Schritte einsehen und ­ändern (Bild 7).
Ein Ändern von vorhandenen Befehlenoder nachträgliches Einfügen von Befehlen in die ­bestehende Abfolge ist möglich(Bild 7) © Autor

Die Sprache M

Die angewendeten Schritte werden zeilenweise mitprotokolliert. Die dahinterliegende Sprache heißt M.Jeder Befehl endet mit einem Komma. Der gesamte Code ist in zwei let … in-Blöcken eingeschlossen. Im let-Teil stehen die einzelnen Zeilen, die von ­einem Namen eingeleitet werden (als Quasivariable, der der Wert einer Funktion übergeben wird). Die Zeilen werden linear von oben nach unten abge­arbeitet – Sprünge sind natürlich möglich. Im in-Teil befindet sich das Ergebnis der Transformation. Der von Power Query erzeugte Code kann modifiziert werden. In Bild 8 sehen Sie ein Code­beispiel.
Der er­weiterte Editormit einem Code­beispiel(Bild 8) © Autor
Es stehen die klassischen Funktionen zur Verfügung:
  • Textfunktionen, darunter zum Beispiel Text.Length, Text.StartsWith, Text.Middle, Text.PositionOf
  • ­Datumsfunktionen, darunter zum ­Beispiel Date.Day, Date.DayOfWeek, Date.QuarterOfYear, DateTime.LocalNow
  • Zahlenfunktionen, darunter zum Beispiel Number.Exp, Number.Sin, Number.Factorial
  • Verzweigungen wie if
Interessanter sind natürlich die Befehle der Tabellen (Table), die eine Manipulation der Tabelle bewirken: AddColumns, CombineColumns, DuplicateColumn, FillDown, RemoveColumns, RenameColumns, RemoveFirstN, RemoveLastN, Sort, SplitColumn …Und schließlich die Funktionen, die einen Zugriff auf Excel-Arbeitsmappen, auf XML- oder JSON-Dateien, auf Datenbanken et cetera ermöglichen: Excel.Workbook, Xml.Document, Sql.Database, OleDb.Query …Statt langer Listen soll hier ein kleines Beispiel folgen, das die Arbeitsweise von M verdeutlicht. Sie sehen es in Listing 1.
Listing 1: Ein Codebeispiel für eine Abfrage auf eine Datei
let <br/>  Quelle = Excel.Workbook(File.Contents(<span class="hljs-string">"D:\Eigene Dateien\SAP01.xlsx"</span>), null, <span class="hljs-literal">true</span>), <br/>  Tabelle1_Sheet = Quelle{[Item=<span class="hljs-string">"Tabelle1"</span>,Kind=<span class="hljs-string">"Sheet"</span>]}[Data], <br/>  #<span class="hljs-string">"Höher gestufte Header"</span> = Table.PromoteHeaders(Tabelle1_Sheet, [PromoteAllScalars=<span class="hljs-literal">true</span>]), <br/>  #<span class="hljs-string">"Geänderter Typ"</span> = Table.TransformColumnTypes(#<span class="hljs-string">"Höher gestufte Header"</span>,{{<span class="hljs-string">"Status:"</span>, <span class="hljs-keyword">type</span> <span class="hljs-type">text}, </span>{<span class="hljs-string">"Column2"</span>, <br/>    <span class="hljs-keyword">type</span> <span class="hljs-type">any}, </span>{<span class="hljs-string">"Column3"</span>, <span class="hljs-keyword">type</span> <span class="hljs-type">any}, </span>{<span class="hljs-string">"Column4"</span>, <span class="hljs-keyword">type</span> <span class="hljs-type">any}, </span>{<span class="hljs-string">"offen"</span>, <span class="hljs-keyword">type</span> <span class="hljs-type">text}, </span>{<span class="hljs-string">"Column6"</span>, <span class="hljs-keyword">type</span> <span class="hljs-type">any}, </span><br/><span class="hljs-type">    </span>{<span class="hljs-string">"Column7"</span>, <span class="hljs-keyword">type</span> <span class="hljs-type">text}, </span>{<span class="hljs-string">"Column8"</span>, Int64.<span class="hljs-keyword">Type</span>}, {<span class="hljs-string">"Column9"</span>, <span class="hljs-keyword">type</span> <span class="hljs-type">text}, </span>{<span class="hljs-string">"vorerfasst"</span>, <span class="hljs-keyword">type</span> <span class="hljs-type">text}, </span><br/><span class="hljs-type">    </span>{<span class="hljs-string">"Column11"</span>, <span class="hljs-keyword">type</span> <span class="hljs-type">any}, </span>{<span class="hljs-string">"Column12"</span>, <span class="hljs-keyword">type</span> <span class="hljs-type">any}, </span>{<span class="hljs-string">"ausgegl."</span>, <span class="hljs-keyword">type</span> <span class="hljs-type">text}, </span>{<span class="hljs-string">"Column14"</span>, <span class="hljs-keyword">type</span> <span class="hljs-type">text}, </span>{<span class="hljs-string">"Column15"</span>, <br/>    <span class="hljs-keyword">type</span> <span class="hljs-type">any}, </span>{<span class="hljs-string">"Column16"</span>, <span class="hljs-keyword">type</span> <span class="hljs-type">text}, </span>{<span class="hljs-string">"Column17"</span>, <span class="hljs-keyword">type</span> <span class="hljs-type">any}, </span>{<span class="hljs-string">"Column18"</span>, <span class="hljs-keyword">type</span> <span class="hljs-type">text}, </span>{<span class="hljs-string">"Column19"</span>, <span class="hljs-keyword">type</span> <span class="hljs-type">text}}), </span><br/><span class="hljs-type">  </span>#<span class="hljs-string">"Andere entfernte Spalten"</span> = Table.SelectColumns(#<span class="hljs-string">"Geänderter Typ"</span>,{<span class="hljs-string">"Column4"</span>, <span class="hljs-string">"Column11"</span>, <span class="hljs-string">"Column12"</span>, <br/>    <span class="hljs-string">"Column15"</span>, <span class="hljs-string">"Column16"</span>, <span class="hljs-string">"Column19"</span>}), <br/>  #<span class="hljs-string">"Entfernte oberste Zeilen"</span> = Table.Skip(#<span class="hljs-string">"Andere entfernte Spalten"</span>,<span class="hljs-number">9</span>), <br/>  #<span class="hljs-string">"Höher gestufte Header1"</span> = Table.PromoteHeaders(#<span class="hljs-string">"Entfernte oberste Zeilen"</span>, [PromoteAllScalars=<span class="hljs-literal">true</span>]), <br/>  #<span class="hljs-string">"Geänderter Typ1"</span> = Table.TransformColumnTypes(#<span class="hljs-string">"Höher gestufte Header1"</span>,{{<span class="hljs-string">"Zuordnung"</span>, <span class="hljs-keyword">type</span> <span class="hljs-type">any}, </span><br/><span class="hljs-type">    </span>{<span class="hljs-string">"Belegdatum"</span>, <span class="hljs-keyword">type</span> <span class="hljs-type">date}, </span>{<span class="hljs-string">"Buch.dat."</span>, <span class="hljs-keyword">type</span> <span class="hljs-type">date}, </span>{<span class="hljs-string">"Betrag in BW"</span>, <span class="hljs-keyword">type</span> <span class="hljs-type">any}, </span>{<span class="hljs-string">"Währg"</span>, <span class="hljs-keyword">type</span> <span class="hljs-type">text}, </span><br/><span class="hljs-type">    </span>{<span class="hljs-string">"Text"</span>, <span class="hljs-keyword">type</span> <span class="hljs-type">text}}), </span><br/><span class="hljs-type">  </span>#<span class="hljs-string">"Entfernte oberste Zeilen1"</span> = Table.Skip(#<span class="hljs-string">"Geänderter Typ1"</span>,<span class="hljs-number">1</span>), <br/>  #<span class="hljs-string">"Geänderter Typ2"</span> = Table.TransformColumnTypes(#<span class="hljs-string">"Entfernte oberste Zeilen1"</span>,{{<span class="hljs-string">"Betrag in BW"</span>, <span class="hljs-keyword">type</span> <span class="hljs-type">text}}), </span><br/><span class="hljs-type">  </span>#<span class="hljs-string">"Hinzugefügte benutzerdefinierte Spalte"</span> = Table.AddColumn(#<span class="hljs-string">"Geänderter Typ2"</span>, <span class="hljs-string">"Betrag korr"</span>, each <span class="hljs-keyword">if</span> Text.<br/>    EndsWith([#<span class="hljs-string">"Betrag in BW"</span>],<span class="hljs-string">"-"</span>) <span class="hljs-keyword">then</span> <br/>  <span class="hljs-string">"-"</span> & Text.Replace([#<span class="hljs-string">"Betrag in BW"</span>], <span class="hljs-string">"-"</span>, <span class="hljs-string">""</span>) <br/>  <span class="hljs-keyword">else</span> <br/>  [#<span class="hljs-string">"Betrag in BW"</span>]), <br/>  #<span class="hljs-string">"Geänderter Typ3"</span> = Table.TransformColumnTypes(#<span class="hljs-string">"Hinzugefügte benutzerdefinierte Spalte"</span>,{{<span class="hljs-string">"Betrag korr"</span>, <br/>    <span class="hljs-keyword">type</span> <span class="hljs-type">number}}), </span><br/><span class="hljs-type">  </span>#<span class="hljs-string">"Entfernte Spalten"</span> = Table.RemoveColumns(#<span class="hljs-string">"Geänderter Typ3"</span>,{<span class="hljs-string">"Betrag in BW"</span>}), <br/>  #<span class="hljs-string">"Geänderter Typ4"</span> = Table.TransformColumnTypes(#<span class="hljs-string">"Entfernte Spalten"</span>,{{<span class="hljs-string">"Zuordnung"</span>, <span class="hljs-keyword">type</span> <span class="hljs-type">text}}), </span><br/><span class="hljs-type">  </span>#<span class="hljs-string">"Ersetzter Wert"</span> = Table.ReplaceValue(#<span class="hljs-string">"Geänderter Typ4"</span>,<span class="hljs-string">"E"</span>,<span class="hljs-string">""</span>,Replacer.ReplaceText,{<span class="hljs-string">"Zuordnung"</span>}) <br/><span class="hljs-keyword">in</span> <br/>  #<span class="hljs-string">"Ersetzter Wert"</span>  
Hierzu einige Erläuterungen: Die Datei D:\Eigene Dateien\SAP01.xlsx wird geöffnet, einige Spalten und Zeilen werden gelöscht, die erste Zeile wird zur Überschriftenzeile, der Datentyp einer Spalte wird zu Text geändert, das falsche Minus an Ende der negativen Zahlen wird nach vorn geholt und der Buchstabe „E“ wird gelöscht (siehe Bild 9a und Bild 9b).
Die zu importierende Datei(links) …(Bild 9a)… und das Ergebnisder abgearbeiteten Abfrage(Bild 9b) © Autor
Diese Zeilen sind leicht verständlich, ebenso wie auch selbst geschriebene if-Funktionen der folgenden Form:
if Text.EndsWith([#"Betrag in BW"],"-") then 
"-" & Text.Replace([#"Betrag in BW"], "-", "") 
else 
[#"Betrag in BW"]), 
Entsprechend hätte man statt Table.ReplaceValue(#”Ge­än­derter Typ4”,”E”,””,Replacer.ReplaceText,{”Zuordnung”}) (lösche den Anfangsbuchstaben „E“) schreiben können:
#"Hinzugefügte benutzerdefinierte Spalte" = Table.
  AddColumn(#"Geänderter Typ1", "ZuordnungOhneE", each 
  if Text.StartsWith([Zuordnung], "E") then 
Text.Middle([Zuordnung], 1) 
else 
[Zuordnung]), 
  #"Geänderter Typ2" = Table.TransformColumnTypes(
  #"Hinzugefügte benutzerdefinierte Spalte",
  {{"ZuordnungOhneE", Int64.Type}}), 
Erläuterung: Wenn die ID der Spalte Zuordnung mit einem „E“ beginnt, wird der Text nach diesem Buchstaben herausgelöst. Anschließend wird die Spalte in einen ganzzahligen Wert konvertiert. Wie Sie sehen, beginnt die Zählung bei 0.

Power-Query-Abfragen mit Visual Studio ­anstoßen, ­generieren und modifizieren

Da die Skriptsprache M ihre Befehle als Text zur Verfügung stellt, können diese Befehle mit einer Programmiersprache erzeugt oder modifiziert werden. In gleicher Weise kann ­eine Aktualisierung durchgeführt werden.Ein Beispiel soll das veranschaulichen. Stellen Sie sich folgendes Szenario vor: Sie erhalten jede Woche einen Download als Excel-Datei mit den aktuellen Preisen Ihrer Produkte. Diese Preise liegen in verschiedenen Währungen vor. Sie sollen in eine Leitwährung – beispielsweise Euro – umgerechnet werden. Hierzu sind die Tageswechselkurse nötig. Diese sollen aus dem Internet geholt und in die aktuelle Excel-Mappe eingefügt werden, und danach zum Beispiel mit einem SVERWEIS verknüpft werden.Power Query kann auf das ­Internet zugreifen und Tabellen einer Seite verarbeiten. Unter [2] befinden sich in mehreren Tabellen die aktuellen Währungskurse. Diese sechs Tabellen werden zusammengefasst, bereinigt, aufbereitet und mit Euro als Leitwährung (Euro zu Euro: Kurs 1) erweitert. Diese Liste wird in die aktuelle Datei in eine neue Tabelle geschrieben. Dort kann sie jederzeit aktualisiert werden, wenn Internetzugriff besteht. Den Code in M sehen Sie in Listing 2.
Listing 2: Greife auf eine Internetseite zu, fasse Tabellen von dort zusammen und schreibe die Daten nach Excel
let <br/>  Quelle = Table.Combine({#"Table 0", #"Table 1", #"Table 2", #"Table 3", #"Table 4", #"Table 5"}), <br/>  #"Entfernte Spalten" = Table.RemoveColumns(Quelle,{"Kürzel", "Veränderung", "Zeit", "Heute", ""}), <br/>  #"Ersetzter Wert" = Table.ReplaceValue(#"Entfernte Spalten","EUR/","",Replacer.ReplaceText,{"Kurs"}), <br/>  #"Spalte nach Position teilen" = Table.SplitColumn(#"Ersetzter Wert", "Kurs", <br/>    Splitter.SplitTextByPositions({0, 3}, false), {"Kurs.1", "Kurs.2"}), <br/>  #"Geänderter Typ" = Table.TransformColumnTypes(#"Spalte nach Position teilen",{{"Kurs.1", type text}, <br/>    {"Kurs.2", type text}}), <br/>  #"Entfernte Spalten1" = Table.RemoveColumns(#"Geänderter Typ",{"Kurs.2"}), <br/>  #"Umbenannte Spalten" = Table.RenameColumns(#"Entfernte Spalten1",{{"Kurs.1", "Währung"}, {"in %", "Kurs"}}), <br/>  #"Euro Einfügen"= Table.InsertRows(#"Umbenannte Spalten", 0, { [Land = "EURO-Länder", Währung = "EUR", <br/>    Kurs = 1] }) <br/>in <br/>  #"Euro Einfügen"  
Da mehrere Tabellen zusammengefügt werden, muss eine Abfrage auf jede dieser Tabellen erstellt werden. Das Grundgerüst in VB.NET sieht folgendermaßen aus:
strZugriffAufTabelle = "let" & Chr(13) & "" & Chr(10) &
  "    Quelle = Web.Page(Web.Contents(""https://www.
  consorsbank.de/ev/Wertpapierhandel/Kurse-Maerkte/
  Waehrungen""))," & Chr(13) & "" & Chr(10) & "    Data
  INDEX = Quelle{INDEX}[Data]," & Chr(13) & "" & Chr(10) 
  & "    #""Geänderter Typ"" = Table.TransformColumn
  Types(DataINDEX,{{""Land"", type text}, {""Kürzel"", 
  type text}, {""Kurs"", type text}, {""in %"", type 
  number}, {""Veränderung"", Percentage.Type}, {""Zeit"",
  type time}, {""" & "Heute"", type text}, {"""", type 
  text}})" & Chr(13) & "" & Chr(10) & "in" & Chr(13) & 
  "" & Chr(10) & "    #""Geänderter Typ""" 
Die Objektvariable xlDatei greift auf die aktuelle Datei zu:
xlDatei = Globals.ThisAddIn.Application.ActiveWorkbook 
Mit einer Schleife wird mithilfe der Variablen strZugriff­AufTabelle jeweils eine Abfrage für jede Tabelle der Internet­seite erzeugt:
For i = 0 To 5 
  xlDatei.Queries.Add(Name:="tbl_Kurstabelle"
  & i, Formula:=Replace(strZugriffAufTabelle, 
  "INDEX", i)) 
Next 
Die neu erzeugten Abfragen tragen nun die Namen tbl_Kurstabelle0, tbl_Kurstabelle1, tbl_Kurstabelle2 …Der eigentliche M-Code wird an die Varia­ble strM_Code übergeben:

strM_Code = "let" & vbCr & "Quelle = Table.
  Combine({#""tbl_Kurstabelle0"", #""tbl_
  Kurstabelle1"", #""tbl_Kurstabelle2"", 
  [...] 
 
Hier wird mit den neuen Namen tbl_Kurstabelle0, tbl_Kurstabelle1 … gearbeitet.Damit wird eine neue Abfrage erzeugt:
xlAbfrage = xlDatei.Queries.Add(Name:=strAbfrageName, 
  Formula:=strM_Code, Descripton:=strAbfrageBeschreibung) 
Eine Verbindung wird hergestellt:
xlDatei.Connections.Add2(Name:="Query - " & xlAbfrage.
  Name, Description:="Connection To the ‚" & xlAbfrage.
  Name & "‘ query in the workbook.", ConnectionString:=
  "OLEDB;Provider=Microsoft.Mashup.OleDb.1;Data Source=
  $Workbook$;Location=" & xlAbfrage.Name, CommandText:=
  """" & xlAbfrage.Name & """", lCmdtype:=6, CreateModel
  Connection:=True, ImportRelationships:=False) 
Das Ganze kann nun als Tabelle in ein Arbeitsblatt geschrieben werden:
xlListObjekt = xlBlatt.ListObjects.Add(SourceType:=4, 
  Source:=xlDatei.Connections("Query - " & 
  xlAbfrage.Name), Destination:=xlBlatt.Range("$A$1")) 
xlTabellenObjekt = xlListObjekt.TableObject 
With xlTabellenObjekt 
  .RowNumbers = False 
  .PreserveFormatting = True 
  .PreserveColumnInfo = False 
  .AdjustColumnWidth = True 
  .RefreshStyle = 1 
  .ListObject.DisplayName = 
    Replace(xlAbfrage.Name, " ", "_") & 
    "_ListObject" 
  .Refresh() 
End With 
Das Ergebnis sehen Sie in Bild 10.
Das Ergebnisdes Internetzugriffs(Bild 10) © Autor
Randbemerkung: Da Power Query Case-sensitiv ist, werden die USA zwischen die Türkei und Ungarn einsortiert: Also gilt: Türkei < USA < Ungarn. Eine Lösung: Man ersetzt „USA“ durch „Usa“, sortiert dann und ersetzt anschließend „Usa“ in „USA“. Oder man ändert in M (im erweiterten Editor) den Sortier­befehl in folgender Weise ab:
#"Sortierte Zeilen" = Table.Sort(#"Umbenannte Spalten",
  {{each Text.Upper([Land]), Order.Ascending}}) 
Dabei steht Umbenannte Spalten für den vorhergehenden Befehl und Land für die zu sortierende Spalte. Die Funktion Text.Upper ändert die Schreibweise der Länderbezeichnungen in Großbuchstaben, und Table.Sort sortiert diese. Anders als in Excel gibt es übrigens in Power Pivot leider keinen Schalter (oder Parameter), um die Sortierung nicht Case-sensitiv durchzuführen.

Bestimmte Tabellen aktualisieren

Ein weiteres Beispiel. Stellen Sie sich folgendes Szenario vor: Sie haben eine Excel-Tabelle, die mit mehreren Dutzend Abfragen aus einer Datenbank (oder mehreren Datenbanken) Daten herausholt, die dann in einem Dashboard aufbereitet werden. Es kann sich hierbei um eine Oracle-Datenbank, ­einen SQL Server oder auch nur um Access handeln. Da nun einige der Tabellen mehrere Millionen Datensätze haben, würde es lange dauern, alle Abfragen mit einem RefreshAll zu aktualisieren. Umgekehrt wäre es auch zeitraubend und fehleranfällig, „per Hand“ die betroffenen Tabellen zu aktualisieren. Abhilfe schafft ein kleines Skript, das überprüft, welche Abfrage von welcher Verbindung verwendet wird, um gezielt diese zu aktualisieren. In Listing 3 sehen Sie die ­Aktualisierungsroutine, die überprüft, ob eine Abfrage ihre Daten aus der Datenbank Nordwind abruft. Bild 11 zeigt das Dashboard, Bild 12 die durchgeführte Aktualisierung.
Listing 3: Aktualisiere alle Verbindungen, die einen Zugriff auf die Datenbank „Nordwind“ vornehmen
Sub AktualisiereNordwind() &lt;br/&gt;  Dim i As Integer &lt;br/&gt;  Dim j As Integer &lt;br/&gt;  Dim intZähler As Integer = 0 &lt;br/&gt;&lt;br/&gt;  Dim xlDatei As Excel.Workbook &lt;br/&gt;  xlDatei = &lt;br/&gt;    Globals.ThisAddIn.Application.ActiveWorkbook &lt;br/&gt;&lt;br/&gt;&lt;br/&gt;  For i = 1 To xlDatei.Queries.Count &lt;br/&gt;    If xlDatei.Queries(i).Formula Like "*Nordwind*" &lt;br/&gt;    Then &lt;br/&gt;      For j = 1 To xlDatei.Connections.Count &lt;br/&gt;        If xlDatei.Connections(j).Name Like "*" &amp;amp; &lt;br/&gt;            xlDatei.Queries(i).Name Then &lt;br/&gt;          xlDatei.Connections(j).Refresh() &lt;br/&gt;          intZähler = intZähler + 1 &lt;br/&gt;        End If &lt;br/&gt;      Next j &lt;br/&gt;    End If &lt;br/&gt;  Next i &lt;br/&gt;&lt;br/&gt;  MsgBox(intZähler &amp;amp; " Abfragen wurden aktualisiert.") &lt;br/&gt;&lt;br/&gt;End Sub  
Die Abfragen, die auf die Datenbank „Nordwind“ aufsetzen, werden aktualisiert(Bild 12) © Autor
Ein Dashboardmit verschiedenen Tabellen(Bild 11) © Autor

Visual Studio als Editor für M

Leider unterstützt der erweiterte Editor weder in Excel noch in Power BI IntelliSense. Zwar soll es in nächster Zeit implementiert werden – aber bis dahin muss man leider den M-Code ohne jede Hilfe selbst erstellen. Ein hierfür praktisches Tool finden Sie jedoch unter [3]. Wenn Sie dieses Add-in ­herunterladen und installieren, steht Ihnen in Visual Studio eine neue Projektvorlage Power Query zur Verfügung (siehe Bild 13). Wenn Sie die Datei PQ file verwenden, erhalten Sie die üblichen Hilfen: IntelliSense, Tooltipps, farbige Code­formatierungen und Fehlerkennzeichnungen. Sie sehen in Bild 14 ein Codebeispiel, das die Arbeitsweise verdeutlicht.
Die Vorlage PQ filefür die bequeme Codeeingabe der Sprache M(Bild 13) © Autor
IntelliSensehilft bei der Codeeingabe(Bild 14) © Autor

Zusammenfassung

Das mächtige Werkzeug Power Query ermöglicht Zugriff auf Datenbanken, auf Dateien, die sich in einem Ordner be­finden, auf das Internet und auf Excel-Tabellen beziehungsweise Text-, XML- oder JSON-Dateien. Diese Daten können aufbereitet und nach Excel zurückgegeben oder in Power BI verarbeitet werden. Die einzelnen Schritte werden mitprotokolliert und können gelöscht, geändert oder ergänzt werden.Die zugrunde liegende Sprache heißt M. Da man mit Vi­sual Studio auf Excel zugreifen beziehungsweise Add-ins für Excel erstellen kann, ist es möglich, solche M-Code-Texte neu zu generieren, vorhandene zu modifizieren oder auch Abfragen auf bestimmte Inhalte zu überprüfen. Ebenso hat man über Visual Studio Zugriff auf die Abfragen (Queries) beziehungsweise auf die Datenverbindungen (Connections), die ebenfalls manipuliert, gelöscht oder erzeugt werden können. Und schließlich stellt Microsoft ein Power Query SDK zur Verfügung, das eine bequeme Erstellung von M-Code ermöglicht.
Projektdateien herunterladen

Fussnoten

  1. Power BI, http://www.dotnetpro.de/SL1908PowerQuery1
  2. Beispieltabellen der aktuellen Währungskurse, http://www.dotnetpro.de/SL1908PowerQuery2
  3. Power Query SDK, http://www.dotnetpro.de/SL1908PowerQuery3

Neueste Beiträge

DWX hakt nach: Wie stellt man Daten besonders lesbar dar?
Dass das Design von Websites maßgeblich für die Lesbarkeit der Inhalte verantwortlich ist, ist klar. Das gleiche gilt aber auch für die Aufbereitung von Daten für Berichte. Worauf besonders zu achten ist, erklären Dr. Ina Humpert und Dr. Julia Norget.
3 Minuten
27. Jun 2025
DWX hakt nach: Wie gestaltet man intuitive User Experiences?
DWX hakt nach: Wie gestaltet man intuitive User Experiences? Intuitive Bedienbarkeit klingt gut – doch wie gelingt sie in der Praxis? UX-Expertin Vicky Pirker verrät auf der Developer Week, worauf es wirklich ankommt. Hier gibt sie vorab einen Einblick in ihre Session.
4 Minuten
27. Jun 2025
„Sieh die KI als Juniorentwickler“
CTO Christian Weyer fühlt sich jung wie schon lange nicht mehr. Woran das liegt und warum er keine Angst um seinen Job hat, erzählt er im dotnetpro-Interview.
15 Minuten
27. Jun 2025
Miscellaneous

Das könnte Dich auch interessieren

Von Excel zu FlexCel - Tabellen mit .NET erstellen
Im Büroalltag gilt Excel als Schweizer Taschenmesser. Die Bibliothek TMS FlexCel befähigt eigene Anwendungen, mit Excel-Tabellen zu arbeiten.
16 Minuten
Hilfsprogramm für VBA-Code - CodeProject, NewPast
Wer sich mit VBA-Code herumschlagen muss, kann jede Hilfe brauchen. Der Office Programming Helper von CodeProject-Member NewPast liefert automatische Einzüge, Zeilennummern, Error Handling und mehr.
2 Minuten
4. Mär 2020
Spezielle Formatierungen - Excel-Zellen mit Python formatieren
Die openpyxl-Bibliothek bietet zahlreiche Funktionen, um Zellbereiche einer Excel-Tabelle mit den gewünschten Formatierungen zu versehen.
9 Minuten
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige