Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
W tym samouczku użyjesz PowerShell do przesłania pojedynczego żądania do interfejsu API REST Execute DAX Queries, zawierającego wiele poleceń EVALUATE, a następnie przeanalizujesz odpowiedź Apache Arrow z wieloma zestawami wyników. Ten wzorzec umożliwia pobranie kilku powiązanych zestawów wyników w jednej rundzie ze skryptu automatyzacji programu PowerShell.
Dlaczego prześlij wiele instrukcji EVALUATE w jednym żądaniu
Interfejs API wykonywania zapytań języka DAX akceptuje jeden query ciąg, który może zawierać wiele EVALUATE instrukcji. Każda instrukcja zwraca własny zestaw wyników, a treść odpowiedzi jest konkatenacją jednego strumienia Arrow IPC dla każdej instrukcji EVALUATE, zgodnie z kolejnością deklaracji. Przesyłanie powiązanych zapytań razem pozwala uniknąć narzutu związanego z każdym żądaniem przy oddzielnych wywołaniach HTTP, w tym dodatkowej walidacji tokenu Microsoft Entra i inicjalizacji aparatu DAX. Wysyłanie wielu EVALUATE poleceń w jednym żądaniu może również pomóc ograniczyć skutki ograniczania liczby żądań. Power BI ogranicza liczbę wywołań do 120 żądań zapytań na minutę na użytkownika na potrzeby operacji zapytań modelu semantycznego.
Co tworzysz
W jednym skrypcie programu PowerShell:
- Uzyskaj token dostępu Microsoft Entra.
- Utwórz treść żądania, której
queryzawiera trzy instrukcjeEVALUATE. - Wyślij żądanie i przechwyć surowy strumień odpowiedzi Arrow IPC.
- Przetwórz odpowiedź na jeden zestaw wyników dla każdej instrukcji
EVALUATE. - Wyświetl każdy zestaw wyników jako obiekty programu PowerShell.
Wymagania wstępne
- Program PowerShell w wersji 7.4 lub nowszej. Program Windows PowerShell 5.1 nie jest obsługiwany, ponieważ pakiet
Apache.Arrowużywany w tym samouczku koliduje z zestawemSystem.Memorydołączonym do programu PowerShell 5.1. - Obszar roboczy Power BI w pojemności Premium lub Fabric z co najmniej jednym modelem semantycznym.
- Uprawnienia kompilacji i odczytu w modelu semantycznym.
- Moduł MicrosoftPowerBIMgmt na potrzeby uwierzytelniania. Polecenia cmdlet używają własnej aplikacji klienckiej Power BI firmy Microsoft, więc nie trzeba rejestrować własnej aplikacji w usłudze Microsoft Entra.
- Biblioteki .NET Apache.Arrow i Apache.Arrow.Compression służące do deserializacji odpowiedzi. Interfejs API REST Execute DAX Queries kompresuje bufory Arrow przy użyciu kompresji ramek LZ4, więc
Apache.Arrow.Compressioni jego zależności (K4os.Compression.LZ4,K4os.Compression.LZ4.Streams,K4os.Hash.xxHash,ZstdSharp.Port) są wymagane. W następnym kroku pokazano, jak je pobrać. - Następujące ustawienia dzierżawy włączone w portalu administracyjnym Power BI:
- Interfejs API REST wykonywania zapytań zestawu danych (w obszarze Ustawienia dewelopera).
- ** Zezwól na punkty końcowe XMLA i analizowanie w Excelu za pomocą lokalnych modeli semantycznych (w obszarze Ustawienia integracji).
Zainstaluj program PowerShell w wersji 7.4 lub nowszej przy użyciu zestawu narzędzi winget:
winget install --id Microsoft.PowerShell --source winget
Po instalacji uruchom nową powłokę poleceniem pwsh. Uruchom pozostałe polecenia w tym samouczku z tej sesji.
Zainstaluj moduł MicrosoftPowerBIMgmt. Przełącznik -Force akceptuje monit dotyczący niezaufanego repozytorium w usłudze Galeria programu PowerShell.
Install-Module -Name MicrosoftPowerBIMgmt -Scope CurrentUser -Force
Pobierz wymagane pakiety NuGet i wyodrębnij ich biblioteki do C:\Tools\Apache.Arrow\. Plik .nupkg to archiwum ZIP, więc Expand-Archive działa na nim bezpośrednio. Pętla wybiera najwyższy netX.0 folder docelowy w każdym pakiecie, aby zestawy były zgodne, ponieważ pakiety publikują nowsze elementy docelowe.
$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 — Uwierzytelnianie
Zaloguj się do usługa Power BI interaktywnie, a następnie wyodrębnij token dostępu. Polecenie cmdlet Connect-PowerBIServiceAccount nie wymaga rejestrowania własnej aplikacji w Microsoft Entra.
Connect-PowerBIServiceAccount -WarningAction SilentlyContinue
$accessToken = (Get-PowerBIAccessToken).Authorization -replace '^Bearer\s+',''
2 — Tworzenie żądania z wieloma instrukcjami EVALUATE
Zdefiniuj obszary robocze i elementy docelowe modelu semantycznego. Następnie skompiluj treść żądania. Właściwość query jest pojedynczym ciągiem, który zawiera trzy EVALUATE instrukcje oddzielone pustymi wierszami.
$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 — Wysyłanie żądania i przechwytywanie nieprzetworzonego strumienia odpowiedzi
Wyślij żądanie POST i odczytaj treść odpowiedzi jako strumień binarny. Użyj HttpWebRequest zamiast Invoke-RestMethod, Invoke-PowerBIRestMethodlub Invoke-WebRequest. Odpowiedź jest binarnym strumieniem IPC Apache Arrow. Polecenia cmdlet programu PowerShell wyższego poziomu interpretują treść odpowiedzi jako tekst, który uszkadza zawartość binarną.
HttpWebRequest zwraca surowy strumień bez modyfikacji.
$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 - Przeanalizuj odpowiedź zawierającą wiele zestawów wyników
Treść odpowiedzi stanowi konkatenacja jednego strumienia IPC Apache Arrow dla każdej instrukcji EVALUATE. PowerShell nie zawiera parsera Arrow, więc w tym kroku ładowana jest biblioteka .NET Apache.Arrow przy użyciu niewielkiej, wbudowanej klasy pomocniczej C# dodanej za pomocą Add-Type. Pozostawienie logiki pętli strumienia danych w języku C# sprawia, że miejsce wywołania jest krótkie i zwraca listę zbiorów wyników, po której skrypt PowerShell może iterować. Pomocnik otwiera nowy ArrowStreamReader po każdym znaczniku końca strumienia, więc ta sama pętla obsługuje dowolną liczbę zestawów wyników w odpowiedzi.
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 — Praca z każdym zestawem wyników
Przekonwertuj każdy zestaw wyników na PSCustomObject wiersze. Teraz możesz przepuścić wiersze przez Where-Object, Group-Object, Export-Csv lub dowolne inne polecenie cmdlet programu PowerShell.
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
Każda zmienna przechowuje wiersze z odpowiedniej EVALUATE instrukcji w kolejności, w której instrukcje są wyświetlane w żądaniu.
Troubleshooting
-
401 Brak autoryzacji — token buforowany wygasł. Uruchom ponownie
Connect-PowerBIServiceAccount, aby je odświeżyć, a następnie ponownie odczytaj$accessTokenzGet-PowerBIAccessToken. -
Ostrzeżenia biblioteki MSAL w trakcie
Connect-PowerBIServiceAccount—MicrosoftPowerBIMgmtzawiera starszą wersję biblioteki MSAL.NET, która emituje wewnętrzne komunikaty śledzenia (na przykładSetAuthorityUri,TryNormalizeRealm,MsaDeviceOperationProvider is not available) na poziomie ostrzeżenia. Można je bezpiecznie zignorować, o ile cmdlet wyświetli blokEnvironment/TenantId/UserName. Aby je wyłączyć, przekaż-WarningAction SilentlyContinue. -
HTTP 200 z błędnym zestawem wyników — Żądanie HTTP zakończyło się powodzeniem, ale strumień Arrow zawiera błąd. Sprawdź metadane schematu elementu
IsError=truei odczytajFaultCodeorazFaultString. Aby uzyskać szczegółowe informacje, zobacz Najlepsze rozwiązania dotyczące interfejsu API REST wykonywania zapytań języka DAX. -
Invoke-RestMethodzwraca zniekształcony tekst — nie używajInvoke-RestMethod,Invoke-PowerBIRestMethodaniInvoke-WebRequestz tym interfejsem API. Odpowiedź jest binarna; użyj poleceniaHttpWebRequest, jak pokazano w kroku 3. -
Add-Typenie może załadowaćApache.Arrow.dll— W programie Windows PowerShell 5.1 pakietApache.Arrowpowoduje konflikt z wbudowanym zestawemSystem.Memory. Użyj programu PowerShell w wersji 7.4 lub nowszej. -
Nie zwrócono żadnych zestawów wyników lub zwrócono ich mniej niż
EVALUATEinstrukcji — Upewnij się, że każdaEVALUATEinstrukcja jest składniowo poprawna sama w sobie. Pojedynczy nieprawidłowy elementEVALUATEpowoduje, że interfejs API zwraca błąd zamiast częściowej odpowiedzi zawierającej wiele zestawów wyników.
Treści powiązane
- Omówienie interfejsu API wykonywania zapytań języka DAX
- Rozpocznij pracę z interfejsem API REST do wykonywania zapytań DAX
- Tutorial: Tworzenie usługi .NET warstwy średniej przy użyciu interfejsu API REST wykonywania zapytań języka DAX
- Samouczek: wielkoskalowa ekstrakcja w Pythonie w notesach Fabric
- Najlepsze rozwiązania dotyczące interfejsu API REST wykonywania zapytań języka DAX
- Dokumentacja referencyjna interfejsu API REST wykonywania zapytań języka DAX