Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
In diesem Lernprogramm verwenden Sie PowerShell, um eine einzelne EXECUTE DAX Queries REST-API-Anforderung zu senden, die mehrere EVALUATE Anweisungen enthält, und analysieren sie dann die Multi-Resultset Apache Arrow-Antwort. Mit diesem Muster können Sie in einem PowerShell-Automatisierungsskript mehrere zusammengehörige Ergebnismengen in einem einzigen Roundtrip abrufen.
Warum mehrere EVALUATE-Anweisungen in einer Anforderung übermittelt werden
Die EXECUTE DAX-Abfragen-API akzeptiert eine einzelne query Zeichenfolge, die mehrere EVALUATE Anweisungen enthalten kann. Jede Anweisung gibt ihre eigene Ergebnismenge zurück, und der Antworttextkörper ist die Verkettung jeweils eines Arrow-IPC-Datenstroms für jede EVALUATE-Anweisung in Deklarationsreihenfolge. Das Zusammensenden verwandter Abfragen vermeidet den Aufwand pro Anforderung getrennter HTTP-Aufrufe, einschließlich zusätzlicher Microsoft Entra Tokenüberprüfung und DAX-Modulinitialisierung. Das Senden mehrerer EVALUATE Anweisungen in einer Anforderung kann auch dazu beitragen, die Auswirkungen der Anforderungseinschränkung zu verringern. Power BI beschränkt Aufrufer auf 120 Abfrageanforderungen pro Minute pro Benutzer für Semantikmodell-Abfragevorgänge.
Was Sie erstellen
In einem PowerShell-Skript führen Sie Folgendes aus:
- Abrufen eines Microsoft Entra Zugriffstokens.
- Erstellen Sie einen Anforderungstext, dessen
querydreiEVALUATEAnweisungen enthält. - Senden Sie die Anfrage und erfassen Sie den rohen Arrow-IPC-Antwortdatenstrom.
- Parsen Sie die Antwort in eine Ergebnismenge pro
EVALUATE-Anweisung. - Zeigt jedes Resultset als PowerShell-Objekte an.
Voraussetzungen
- PowerShell 7.4 oder höher. Windows PowerShell 5.1 wird nicht unterstützt, da das
Apache.Arrowin diesem Lernprogramm verwendete Paket mit derSystem.Memoryin PowerShell 5.1 enthaltenen Assembly in Konflikt steht. - Ein Power-BI-Arbeitsbereich in Premium- oder Fabric-Kapazität mit mindestens einem Semantikmodell.
- Erstellen und Lesen von Berechtigungen für das semantische Modell.
- Das MicrosoftPowerBIMgmt-Modul für die Authentifizierung. Die Cmdlets verwenden die Erstanbieter-Power BI-Client-App von Microsoft, sodass Sie Ihre eigene App nicht in Microsoft Entra registrieren müssen.
- Die Apache.Arrow- und Apache.Arrow.Compression-.NET-Bibliotheken zum Deserialisieren der Antwort. Die REST-API zum Ausführen von DAX-Abfragen komprimiert Arrow-Puffer mit LZ4-Frame-Komprimierung, sodass
Apache.Arrow.Compressionund seine Abhängigkeiten (K4os.Compression.LZ4,K4os.Compression.LZ4.Streams,K4os.Hash.xxHash,ZstdSharp.Port) erforderlich sind. Im nächsten Schritt wird gezeigt, wie sie heruntergeladen werden. - Die folgenden Mandanteneinstellungen sind im Power BI Admin-Portal aktiviert:
- Rest-API für Datasetausführungsabfragen (unter Entwicklereinstellungen).
- Ermöglichen von XMLA-Endpunkten sowie der Analyse in Excel mit lokalen semantischen Modellen (unter Integrationseinstellungen).
Installieren Sie PowerShell 7.4 oder höher mithilfe von Winget:
winget install --id Microsoft.PowerShell --source winget
Starten Sie nach der Installation die neue Shell mit pwsh. Führen Sie die restlichen Befehle in diesem Tutorial in dieser Sitzung aus.
Installieren Sie das MicrosoftPowerBIMgmt-Modul. Der -Force Schalter bestätigt die Aufforderung zu einem nicht vertrauenswürdigen Repository in der PowerShell-Katalog.
Install-Module -Name MicrosoftPowerBIMgmt -Scope CurrentUser -Force
Laden Sie die erforderlichen NuGet-Pakete herunter, und extrahieren Sie ihre Assemblys in C:\Tools\Apache.Arrow\. Eine .nupkg-Datei ist ein ZIP-Archiv, daher funktioniert Expand-Archive direkt damit. Die Schleife wählt den höchsten netX.0 Zielordner in jedem Paket aus, sodass die Assemblys kompatibel bleiben, wenn die Pakete neuere Ziele veröffentlichen.
$dest = "C:\Tools\Apache.Arrow"
New-Item -ItemType Directory -Force -Path $dest | Out-Null
$packages = @(
"Apache.Arrow",
"Apache.Arrow.Compression",
"K4os.Compression.LZ4",
"K4os.Compression.LZ4.Streams",
"K4os.Hash.xxHash",
"ZstdSharp.Port"
)
foreach ($pkg in $packages) {
$nupkg = Join-Path $env:TEMP "$pkg.nupkg"
$expand = Join-Path $env:TEMP $pkg
if (Test-Path $expand) { Remove-Item $expand -Recurse -Force }
Invoke-WebRequest -Uri "https://www.nuget.org/api/v2/package/$pkg" -OutFile $nupkg
Expand-Archive -Path $nupkg -DestinationPath $expand -Force
$libDirs = Get-ChildItem (Join-Path $expand "lib") -Directory
$best = $libDirs | Where-Object { $_.Name -match "^net\d" } |
Sort-Object Name -Descending | Select-Object -First 1
if (-not $best) {
$best = $libDirs | Sort-Object Name -Descending | Select-Object -First 1
}
Get-ChildItem (Join-Path $best.FullName "*.dll") |
Copy-Item -Destination $dest -Force
}
1 – Authentifizieren
Melden Sie sich interaktiv beim Power BI-Dienst an, und extrahieren Sie dann ein Zugriffstoken. Das Connect-PowerBIServiceAccount Cmdlet erfordert nicht, dass Sie Ihre eigene App in Microsoft Entra registrieren.
Connect-PowerBIServiceAccount -WarningAction SilentlyContinue
$accessToken = (Get-PowerBIAccessToken).Authorization -replace '^Bearer\s+',''
2 – Erstellen einer Anforderung mit mehreren EVALUATE-Anweisungen
Definieren Sie die Arbeitsbereichs- und Semantikmodellziele. Erstellen Sie dann den Request-Body. Die query-Eigenschaft ist eine einzelne Zeichenfolge, die drei durch Leerzeilen getrennte EVALUATE-Anweisungen enthält.
$groupId = "YOUR_WORKSPACE_ID"
$datasetId = "YOUR_DATASET_ID"
$query = @"
EVALUATE
ROW("RowCount", COUNTROWS('Sales'))
EVALUATE
TOPN(10, 'Sales', 'Sales'[Amount], DESC)
EVALUATE
SUMMARIZECOLUMNS(
'Date'[Year],
"TotalSales", SUM('Sales'[Amount]))
"@
$body = @{
query = $query
resultsetRowcountLimit = 500000
} | ConvertTo-Json
3 – Senden der Anforderung und Erfassen des rohen Antwortdatenstroms
Senden Sie die POST-Anforderung, und lesen Sie den Antworttext als binären Datenstrom. Verwenden Sie HttpWebRequest anstelle von Invoke-RestMethod, Invoke-PowerBIRestMethod oder Invoke-WebRequest. Die Antwort ist ein binärer Arrow-IPC-Datenstrom. Die PowerShell-Cmdlets auf höherer Ebene interpretieren Antworttexte als Text, wodurch binäre Inhalte beschädigt werden.
HttpWebRequest gibt den unmodifizierten Datenstrom zurück.
$url = "https://api.powerbi.com/v1.0/myorg/groups/$groupId" +
"/datasets/$datasetId/executeDaxQueries"
$request = [System.Net.HttpWebRequest]::Create($url)
$request.Method = "POST"
$request.ContentType = "application/json"
$request.Accept = "application/vnd.apache.arrow.stream"
$request.Timeout = 180000 # milliseconds
$request.Headers.Add("Authorization", "Bearer $accessToken")
$bodyBytes = [System.Text.Encoding]::UTF8.GetBytes($body)
$requestStream = $request.GetRequestStream()
$requestStream.Write($bodyBytes, 0, $bodyBytes.Length)
$requestStream.Close()
$response = $request.GetResponse()
$responseStream = $response.GetResponseStream()
# Buffer the response into memory so the parser can iterate over multiple Arrow IPC streams.
$memoryStream = New-Object System.IO.MemoryStream
$responseStream.CopyTo($memoryStream)
$responseStream.Close()
$response.Close()
$memoryStream.Position = 0
4 – Analysieren der Multi-Result-Set-Antwort
Der Antworttext ist die Verkettung eines Apache Arrow IPC-Datenstroms pro EVALUATE Anweisung. PowerShell enthält keinen Arrow-Parser, daher lädt dieser Schritt die .NET-Bibliothek über einen kleinen Inline-C#-Helper, der mit Add-Type hinzugefügt wird, durch Apache.Arrow. Das Beibehalten der Stream-Loop-Logik in C# hält die Aufrufstelle kurz und gibt eine Liste von Ergebnismengen zurück, die Ihr PowerShell-Skript iterieren kann. Die Hilfsfunktion öffnet nach jeder End-of-Stream-Markierung eine neue ArrowStreamReader, sodass dieselbe Schleife eine beliebige Anzahl von Ergebnismengen in der Antwort verarbeiten kann.
Add-Type -Path "C:\Tools\Apache.Arrow\Apache.Arrow.dll"
Add-Type -Path "C:\Tools\Apache.Arrow\Apache.Arrow.Compression.dll"
# Reference the full .NET reference set that ships with PowerShell 7 so the
# inline C# below can resolve BCL types such as List<T> and Dictionary<,>.
$refs = Get-ChildItem "$PSHOME\ref\*.dll" | ForEach-Object FullName
$refs += Get-ChildItem "C:\Tools\Apache.Arrow\*.dll" | ForEach-Object FullName
Add-Type -ReferencedAssemblies $refs -IgnoreWarnings -WarningAction SilentlyContinue -TypeDefinition @"
using System;
using System.Collections.Generic;
using System.IO;
using Apache.Arrow;
using Apache.Arrow.Compression;
using Apache.Arrow.Ipc;
public class DaxResultSet
{
public List<string> ColumnNames = new List<string>();
public List<Dictionary<string, object>> Rows =
new List<Dictionary<string, object>>();
}
public static class DaxMultiResultReader
{
public static List<DaxResultSet> ReadAll(Stream stream)
{
var results = new List<DaxResultSet>();
var codecFactory = new CompressionCodecFactory();
while (stream.Position < stream.Length)
{
var rs = new DaxResultSet();
bool gotSchema = false;
using (var reader = new ArrowStreamReader(stream, codecFactory, leaveOpen: true))
{
RecordBatch batch;
while ((batch = reader.ReadNextRecordBatch()) != null)
{
using (batch)
{
if (!gotSchema)
{
foreach (var f in batch.Schema.FieldsList)
rs.ColumnNames.Add(f.Name);
gotSchema = true;
}
for (int r = 0; r < batch.Length; r++)
{
var row = new Dictionary<string, object>();
for (int c = 0; c < batch.ColumnCount; c++)
row[rs.ColumnNames[c]] = GetValue(batch.Column(c), r);
rs.Rows.Add(row);
}
}
}
}
if (gotSchema) results.Add(rs);
}
return results;
}
private static object GetValue(IArrowArray a, int i)
{
if (a == null) return null;
if (a is DictionaryArray da)
{
// Resolve the dictionary index, then look up the value in the dictionary.
int dictIndex;
switch (da.Indices)
{
case Int32Array idx32: if (idx32.IsNull(i)) return null; dictIndex = idx32.GetValue(i).Value; break;
case Int16Array idx16: if (idx16.IsNull(i)) return null; dictIndex = idx16.GetValue(i).Value; break;
case Int8Array idx8: if (idx8.IsNull(i)) return null; dictIndex = idx8.GetValue(i).Value; break;
case Int64Array idx64: if (idx64.IsNull(i)) return null; dictIndex = (int)idx64.GetValue(i).Value; break;
default: return da.Indices.ToString();
}
return GetValue(da.Dictionary, dictIndex);
}
if (a is StringArray sa) return sa.GetString(i);
if (a is BooleanArray ba) return ba.IsNull(i) ? (object)null : ba.GetValue(i);
if (a is Int64Array i64) return i64.IsNull(i) ? (object)null : i64.GetValue(i);
if (a is Int32Array i32) return i32.IsNull(i) ? (object)null : i32.GetValue(i);
if (a is DoubleArray d) return d.IsNull(i) ? (object)null : d.GetValue(i);
if (a is Decimal128Array dec) return dec.GetValue(i);
if (a is Date32Array d32) return d32.GetDateTime(i);
if (a is Date64Array d64) return d64.GetDateTime(i);
if (a is TimestampArray ts) return ts.GetTimestamp(i);
return a.ToString();
}
}
"@
$results = [DaxMultiResultReader]::ReadAll($memoryStream)
Write-Host "Received $($results.Count) result sets."
5 – Arbeiten mit jedem Resultset
Wandeln Sie jede Ergebnismenge in PSCustomObject Zeilen um. Jetzt können Sie die Zeilen über Where-Object, Group-Object, , Export-Csvoder ein beliebiges anderes PowerShell-Cmdlet weiterleiten.
function ConvertTo-PSObjectRows {
param([Parameter(Mandatory)] $ResultSet)
foreach ($row in $ResultSet.Rows) {
$obj = [ordered]@{}
foreach ($col in $ResultSet.ColumnNames) { $obj[$col] = $row[$col] }
[PSCustomObject]$obj
}
}
$rowCount = ConvertTo-PSObjectRows -ResultSet $results[0]
$topProducts = ConvertTo-PSObjectRows -ResultSet $results[1]
$yearTotals = ConvertTo-PSObjectRows -ResultSet $results[2]
$rowCount | Format-Table
$topProducts | Format-Table
$yearTotals | Format-Table
Jede Variable enthält die Zeilen aus der entsprechenden EVALUATE Anweisung, in der Reihenfolge, in der die Anweisungen in der Anforderung angezeigt werden.
Problembehandlung
-
401 Nicht autorisiert – Das zwischengespeicherte Token ist abgelaufen. Führen Sie
Connect-PowerBIServiceAccounterneut aus, um die Anzeige zu aktualisieren, und lesen Sie$accessTokendann erneut vonGet-PowerBIAccessToken. -
MSAL-Warnungen während
Connect-PowerBIServiceAccount—MicrosoftPowerBIMgmtenthält eine ältere Version von MSAL.NET, die interne Tracemeldungen (zum BeispielSetAuthorityUri,TryNormalizeRealm,MsaDeviceOperationProvider is not available) mit dem Schweregrad „Warnung“ ausgibt. Sie sind sicher zu ignorieren, solange das Cmdlet denEnvironment/TenantId/UserNameBlock druckt. Um sie zu unterdrücken, übergeben Sie-WarningAction SilentlyContinue. -
HTTP 200 mit einem Fehlerergebnissatz – Die HTTP-Anforderung war erfolgreich, der Arrow-Datenstrom weist jedoch einen Fehler auf. Überprüfen Sie die Schemametadaten für
IsError=true, und lesen SieFaultCodeundFaultString. Ausführliche Informationen finden Sie unter Bewährte Methoden für die REST-API "DAX-Abfragen ausführen". -
Invoke-RestMethodgibt verstümmelten Text zurück — Verwenden SieInvoke-RestMethod,Invoke-PowerBIRestMethododerInvoke-WebRequestnicht mit dieser API. Die Antwort ist binär; verwenden SieHttpWebRequest, wie in Schritt 3 gezeigt. -
Add-Typekann nicht geladen werdenApache.Arrow.dll— Unter Windows PowerShell 5.1 steht das PaketApache.Arrowin Konflikt mit der systemseitig enthaltenenSystem.Memory-Assembly. Verwenden Sie PowerShell 7.4 oder höher. -
Keine oder weniger Ergebnismengen zurückgegeben als
EVALUATE-Anweisungen – Vergewissern Sie sich, dass jedeEVALUATE-Anweisung für sich genommen syntaktisch gültig ist. Ein einzelnes ungültigesEVALUATEführt dazu, dass die API einen Fehler statt einer teilweisen Antwort mit mehreren Ergebnismengen zurückgibt.
Verwandte Inhalte
- Grundlegendes zur API zum Ausführen von DAX-Abfragen
- Erste Schritte mit der REST-API "DAX-Abfragen ausführen"
- Tutorial: Erstellen Eines .NET Mid-Tier-Diensts mit der REST-API "DAX-Abfragen ausführen
- Tutorial: Umfangreiche Python-Extraktion in Fabric-Notebooks
- Bewährte Methoden für die REST-API zum Ausführen von DAX-Abfragen
- REST-API-Referenz für DAX-Abfragen ausführen