Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
I den här självstudien använder du PowerShell för att skicka en enda REST API-begäran till Execute DAX Queries som innehåller flera EVALUATE satser och sedan tolka Apache Arrow-svaret med flera resultatuppsättningar. Med det här mönstret kan du hämta flera relaterade resultatuppsättningar i en tur och retur från ett PowerShell-automatiseringsskript.
Varför skicka in flera EVALUATE-satser i en och samma begäran
API:et för att köra DAX-frågor accepterar en enda query-sträng som kan innehålla flera EVALUATE-satser. Varje sats returnerar sin egen resultatuppsättning, och svarsinnehållet är en sammanfogning av en Arrow IPC-ström per EVALUATE sats i deklarationsordning. Om du skickar relaterade frågor tillsammans undviker du kostnaden per begäran för separata HTTP-anrop, inklusive extra Microsoft Entra tokenverifiering och DAX-motorinitiering. Att skicka flera EVALUATE instruktioner i en begäran kan också bidra till att minska effekten av begränsning av begäranden. Power BI begränsar anropare till 120 frågebegäranden per minut per användare för semantiska modellfrågeåtgärder.
Det här skapar du
I ett PowerShell-skript:
- Hämta en Microsoft Entra åtkomsttoken.
- Skapa en begärandetext vars
queryinnehåller treEVALUATEinstruktioner. - Skicka begäran och fånga den råa Arrow IPC-svarsströmmen.
- Tolka svaret i en resultatuppsättning per
EVALUATE-sats. - Visa varje resultatuppsättning som PowerShell-objekt.
Förutsättningar
- PowerShell 7.4 eller senare. Windows PowerShell 5.1 stöds inte eftersom
Apache.Arrowpaketet som används i den här självstudien står i konflikt medSystem.Memorysammansättningen som ingår i PowerShell 5.1. - En Power BI-arbetsyta i Premium- eller Fabric-kapacitet med minst en semantisk modell.
- Skapa och läsa behörigheter för semantikmodellen.
- MicrosoftPowerBIMgmt-modulen för autentisering. Cmdletarna använder Microsofts förstaparts Power BI-klientapp, så du behöver inte registrera din egen app i Microsoft Entra.
-
Apache.Arrow- och Apache.Arrow.Compression .NET-biblioteken för att deserialisera svaret. REST-API:et för att köra DAX-frågor komprimerar Arrow-buffertar med LZ4-framekomprimering, så
Apache.Arrow.Compressionoch dess beroenden (K4os.Compression.LZ4,K4os.Compression.LZ4.Streams,K4os.Hash.xxHash,ZstdSharp.Port) krävs. Nästa steg visar hur du laddar ned dem. - Följande klientinställningar aktiverade i administratörsportalen för Power BI:
- REST-API för att köra frågor mot datauppsättningar (under Utvecklarinställningar).
- Tillåt XMLA-slutpunkter och Analysera i Excel med lokala semantiska modeller (under Integreringsinställningar).
Installera PowerShell 7.4 eller senare med winget:
winget install --id Microsoft.PowerShell --source winget
Efter installationen startar du det nya gränssnittet med pwsh. Kör de återstående kommandona i den här självstudien från den sessionen.
Installera modulen MicrosoftPowerBIMgmt. Flaggan -Force godtar frågan om ej betrodd lagringsplats i PowerShell Gallery.
Install-Module -Name MicrosoftPowerBIMgmt -Scope CurrentUser -Force
Ladda ned nödvändiga NuGet-paket och extrahera deras sammansättningar till C:\Tools\Apache.Arrow\. En .nupkg fil är ett ZIP-arkiv, så Expand-Archive fungerar direkt på den. Loopen väljer den högsta netX.0 målmappen i varje paket så att sammansättningarna förblir kompatibla när paketen publicerar nyare mål.
$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 – Autentisera
Logga in på služba Power BI interaktivt och extrahera sedan en åtkomsttoken. Cmdleten Connect-PowerBIServiceAccount kräver inte att du registrerar din egen app i Microsoft Entra.
Connect-PowerBIServiceAccount -WarningAction SilentlyContinue
$accessToken = (Get-PowerBIAccessToken).Authorization -replace '^Bearer\s+',''
2 – Skapa en begäran med flera EVALUATE-satser
Definiera mål för arbetsyta och semantisk modell. Skapa sedan begärandetexten. Egenskapen query är en enskild sträng som innehåller tre EVALUATE instruktioner avgränsade med tomma rader.
$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 – Skicka begäran och samla in råsvarsströmmen
Skicka POST-begäran och läs svarstexten som en binär dataström. Använd HttpWebRequest i stället Invoke-RestMethodför , Invoke-PowerBIRestMethodeller Invoke-WebRequest. Svaret är en binär Arrow IPC-ström. PowerShell-cmdletarna på högre nivå tolkar svarskroppar som text, vilket skadar binärt innehåll.
HttpWebRequest returnerar råströmmen oförändrad.
$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 – Parsa svaret för flera resultatuppsättningar
Svarstexten är sammanlänkningen av en Apache Arrow IPC-ström per EVALUATE instruktion. PowerShell innehåller inte någon Arrow-parser, så i det här steget laddas .NET-biblioteket Apache.Arrow in via en liten infogad C#-hjälpklass som lagts till med Add-Type. Om du behåller stream-loop-logiken i C# blir anropswebbplatsen kort och returnerar en lista över resultatuppsättningar som PowerShell-skriptet kan iterera. Hjälpfunktionen öppnar en ny ArrowStreamReader efter varje markör för slut på strömmen, så samma loop hanterar hur många resultatmängder som helst i svaret.
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 – Arbeta med varje resultatuppsättning
Konvertera varje resultatuppsättning till PSCustomObject rader. Nu kan du skicka raderna via Where-Object, Group-Object, Export-Csveller någon annan PowerShell-cmdlet.
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
Varje variabel innehåller raderna från motsvarande EVALUATE instruktion, i den ordning som uttrycken visas i begäran.
Troubleshooting
-
401 Obehörig – den cachelagrade token har upphört att gälla. Kör
Connect-PowerBIServiceAccountigen för att uppdatera den och läs$accessTokensedan igen frånGet-PowerBIAccessToken. -
MSAL-varningar under
Connect-PowerBIServiceAccount—MicrosoftPowerBIMgmtinnehåller en äldre version av MSAL.NET som avger interna spårningsmeddelanden (till exempelSetAuthorityUri,TryNormalizeRealm,MsaDeviceOperationProvider is not available) på varningsnivå. De är säkra att ignorera så länge cmdleten skriver utEnvironment/TenantId/UserNameblocket. Om du vill förhindra dem skickar du-WarningAction SilentlyContinue. -
HTTP 200 med en felresultatuppsättning – HTTP-begäran lyckades, men Pilströmmen har ett fel. Granska schemametadata för
IsError=trueoch läsFaultCodeochFaultString. Mer information finns i Bästa praxis för REST-API:t Köra DAX-frågor. -
Invoke-RestMethodreturnerar förvrängd text – AnvändInvoke-RestMethodinte ,Invoke-PowerBIRestMethodellerInvoke-WebRequestmed det här API:et. Svaret är binärt. användHttpWebRequestsom du ser i steg 3. -
Add-Typekan inte laddasApache.Arrow.dll— I Windows PowerShell 5.1 stårApache.Arrow-paketet i konflikt med den inbyggdaSystem.Memory-sammansättningen. Använd PowerShell 7.4 eller senare. -
Inga eller färre resultatuppsättningar returnerade än
EVALUATE-instruktioner – Bekräfta att varjeEVALUATEinstruktion är syntaktiskt giltig på egen hand. En enda ogiltigEVALUATEgör att API:et returnerar ett fel i stället för ett partiellt svar med flera resultatuppsättningar.