Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Dans ce tutoriel, vous utilisez PowerShell pour soumettre une requête unique à l’API REST Execute DAX Queries qui contient plusieurs instructions EVALUATE, puis vous analysez la réponse Apache Arrow comportant plusieurs jeux de résultats. Ce modèle vous permet de récupérer plusieurs jeux de résultats associés en un aller-retour à partir d’un script d’automatisation PowerShell.
Pourquoi envoyer plusieurs instructions EVALUATE dans une seule requête
L’API Exécuter des requêtes DAX accepte une seule query chaîne qui peut contenir plusieurs EVALUATE instructions. Chaque instruction retourne son propre jeu de résultats, et le corps de la réponse est la concaténation d’un flux Arrow IPC pour chaque instruction EVALUATE, dans l’ordre de déclaration. L’envoi de requêtes associées permet d’éviter la surcharge par requête des appels HTTP distincts, notamment la validation de jetons supplémentaire Microsoft Entra et l’initialisation du moteur DAX. L’envoi de plusieurs EVALUATE instructions au sein d’une même requête peut également aider à atténuer l’impact de la limitation du débit des requêtes. Power BI limite les appelants à 120 requêtes par minute par utilisateur pour les opérations de requête de modèle sémantique.
Ce que vous construisez
Dans un script PowerShell, vous devez :
- Acquérir un jeton d’accès Microsoft Entra.
- Générez un corps de requête dont le
querycontient trois instructionsEVALUATE. - Envoyez la requête et capturez le flux brut de réponse IPC Arrow.
- Analysez la réponse en un jeu de résultats pour chaque instruction
EVALUATE. - Affichez chaque jeu de résultats en tant qu’objets PowerShell.
Prerequisites
- PowerShell 7.4 ou version ultérieure. Windows PowerShell 5.1 n’est pas pris en charge, car le package
Apache.Arrowutilisé dans ce didacticiel entre en conflit avec l’assemblySystem.Memoryincluse dans PowerShell 5.1. - Un espace de travail Power BI sur une capacité Premium ou Fabric avec au moins un modèle sémantique.
- Générer et lire des autorisations sur le modèle sémantique.
- Module MicrosoftPowerBIMgmt pour l’authentification. Les applets de commande utilisent l'application cliente Power BI première partie de Microsoft. Vous n'avez donc pas besoin d'inscrire votre propre application dans Microsoft Entra.
- Les bibliothèques .NET Apache.Arrow et Apache.Arrow.Compression pour désérialiser la réponse. L’API REST Execute DAX Queries compresse les tampons Arrow à l’aide de la compression par trames LZ4 ;
Apache.Arrow.Compressionet ses dépendances (K4os.Compression.LZ4,K4os.Compression.LZ4.Streams,K4os.Hash.xxHash,ZstdSharp.Port) sont donc requis. L’étape suivante montre comment les télécharger. - Les paramètres de locataire suivants sont activés dans le portail d’administration Power BI :
- API REST d’exécution de requêtes sur un jeu de données (sous les paramètres du développeur).
- Autoriser les points de terminaison XMLA et analyser dans Excel avec des modèles sémantiques sur site (sous Paramètres d'intégration).
Installez PowerShell 7.4 ou version ultérieure à l’aide de winget :
winget install --id Microsoft.PowerShell --source winget
Après l’installation, lancez le nouvel interpréteur de commandes avec pwsh. Exécutez les commandes restantes dans ce tutoriel à partir de cette session.
Installez le module MicrosoftPowerBIMgmt. L’indicateur -Force accepte l’invite concernant le dépôt non approuvé de PowerShell Gallery.
Install-Module -Name MicrosoftPowerBIMgmt -Scope CurrentUser -Force
Téléchargez les packages NuGet requis et extrayez leurs fichiers d’assembly dans C:\Tools\Apache.Arrow\. Un .nupkg fichier est une archive ZIP. Expand-Archive Il fonctionne donc directement dessus. La boucle sélectionne le dossier cible le plus élevé netX.0 dans chaque package afin que les assemblys restent compatibles lorsque les packages publient des cibles plus récentes.
$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 - S’authentifier
Connectez-vous au service Power BI de manière interactive, puis extrayez un jeton d’accès. LConnect-PowerBIServiceAccount'applet de commande ne vous oblige pas à inscrire votre propre application dans Microsoft Entra.
Connect-PowerBIServiceAccount -WarningAction SilentlyContinue
$accessToken = (Get-PowerBIAccessToken).Authorization -replace '^Bearer\s+',''
2 - Créer une requête avec plusieurs instructions EVALUATE
Définissez les cibles de l’espace de travail et du modèle sémantique. Générez ensuite le corps de la requête. La query propriété est une chaîne unique qui contient trois EVALUATE instructions séparées par des lignes vides.
$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 - Envoyer la requête et capturer le flux de réponse brut
Envoyez la requête POST et lisez le corps de la réponse en tant que flux binaire. Utiliser HttpWebRequest plutôt que Invoke-RestMethod, Invoke-PowerBIRestMethodou Invoke-WebRequest. La réponse est un flux IPC binaire Arrow. Les applets de commande PowerShell de niveau supérieur interprètent les corps de réponse en tant que texte, ce qui endommage le contenu binaire.
HttpWebRequest retourne le flux brut non modifié.
$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 - Analyser la réponse contenant plusieurs jeux de résultats
Le corps de la réponse est la concaténation d’un flux IPC Apache Arrow pour chaque instruction EVALUATE. PowerShell n'est pas fourni avec un analyseur Arrow. Cette étape charge donc la Apache.Arrow bibliothèque .NET via un petit helper C# inline ajouté avec Add-Type. La conservation de la logique de boucle de flux en C# permet de raccourcir le site d’appel et retourne une liste de jeux de résultats que votre script PowerShell peut itérer. La fonction utilitaire ouvre un nouveau ArrowStreamReader après chaque marqueur de fin de flux, ainsi la même boucle gère un nombre quelconque d’ensembles de résultats dans la réponse.
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 - Travailler avec chaque jeu de résultats
Limitez chaque jeu de résultats à PSCustomObject lignes. Vous pouvez maintenant faire passer les lignes via Where-Object, Group-Object, Export-Csv ou n’importe quelle autre cmdlet 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
Chaque variable contient les lignes de l’instruction correspondante EVALUATE , dans l’ordre dans lequel les instructions apparaissent dans la requête.
Résolution des problèmes
-
401 Non autorisé : le jeton mis en cache a expiré. Exécutez de nouveau
Connect-PowerBIServiceAccountpour l’actualiser, puis relisez$accessTokendepuisGet-PowerBIAccessToken. -
Avertissements MSAL pendant
Connect-PowerBIServiceAccount—MicrosoftPowerBIMgmtregroupe une MSAL.NET plus ancienne qui émet des messages de trace internes (par exemple,SetAuthorityUri,TryNormalizeRealm,MsaDeviceOperationProvider is not available) au niveau de gravité de l’avertissement. Ils sont sûrs d’être ignorés tant que l’applet de commande imprime leEnvironment/TenantId/UserNamebloc. Pour les supprimer, passez-WarningAction SilentlyContinue. -
HTTP 200 avec un ensemble de résultats d’erreur — La requête HTTP a réussi, mais le flux Arrow contient une erreur. Inspectez les métadonnées de schéma pour
IsError=true, et lisezFaultCodeetFaultString. Pour plus de détails, consultez les meilleures pratiques pour l’API REST d’exécution de requêtes DAX. -
Invoke-RestMethodretourne du texte garbré : n’utilisezInvoke-RestMethodpas,Invoke-PowerBIRestMethodouInvoke-WebRequestavec cette API. La réponse est binaire ; utilisezHttpWebRequestcomme indiqué à l’étape 3. -
Add-Typene parvient pas à chargerApache.Arrow.dll: sur Windows PowerShell 5.1, leApache.Arrowpackage est en conflit avec l’assembly in-boxSystem.Memory. Utilisez PowerShell 7.4 ou version ultérieure. -
Pas ou moins de jeux de résultats retournés que
EVALUATEles instructions : vérifiez que chaqueEVALUATEinstruction est syntactiquement valide par lui-même. Un seulEVALUATEnon valide amène l’API à renvoyer une erreur plutôt qu’une réponse partielle contenant plusieurs jeux de résultats.
Contenu connexe
- Comprendre l’API d’exécution de requêtes DAX
- Commencer avec l'API REST pour exécuter des requêtes DAX
- Tutoriel : Créer un service de niveau intermédiaire .NET avec l’API REST "Execute DAX Queries"
- Tutoriel : extractions Python à grand volume dans les notebooks de Fabric
- Meilleures pratiques pour l'exécution des requêtes DAX de l'API REST
- Informations de référence sur l’API REST d’exécution des requêtes DAX