Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Neste tutorial, utiliza o PowerShell para submeter um único pedido à Execute DAX Queries REST API que contém várias EVALUATE instruções e, em seguida, analisar a resposta Apache Arrow com vários conjuntos de resultados. Este padrão permite-lhe recuperar vários conjuntos de resultados relacionados numa única viagem de ida e volta a partir de um script de automação PowerShell.
Porque submeter múltiplas declarações EVALUATE num só pedido
A API Execute DAX Queries aceita uma única query string que pode conter múltiplas EVALUATE instruções. Cada instrução retorna o seu próprio conjunto de resultados, e o corpo da resposta é a concatenação de um fluxo IPC Arrow por instrução EVALUATE, na ordem de declaração. Enviar consultas relacionadas em conjunto evita a sobrecarga associada a cada pedido de chamadas HTTP separadas, incluindo a validação adicional de tokens do Microsoft Entra e a inicialização do motor DAX. Enviar múltiplas EVALUATE instruções num único pedido também pode ajudar a mitigar o impacto da limitação dos pedidos. O Power BI limita os chamadores a 120 pedidos de consulta por minuto por utilizador para operações de consulta de modelos semânticos.
O que você constrói
Num script PowerShell, você:
- Adquira um token de acesso Microsoft Entra.
- Constrói um corpo de pedido que
querycontenha trêsEVALUATEinstruções. - Envie a solicitação e capte o fluxo de resposta Arrow IPC em bruto.
- Analise a resposta num único conjunto de resultados por
EVALUATEafirmação. - Mostrar cada conjunto de resultados como objetos PowerShell.
Pré-requisitos
- PowerShell 7.4 ou posterior. O PowerShell 5.1 do Windows não é suportado porque o
Apache.Arrowpacote usado neste tutorial entra em conflito com oSystem.Memoryassembly incluído no PowerShell 5.1. - Um espaço de trabalho Power BI em capacidade Premium ou Fabric com pelo menos um modelo semântico.
- Permissões de construção e leitura no modelo semântico.
- O módulo MicrosoftPowerBIMgmt para autenticação. Os cmdlets utilizam a aplicação cliente Power BI da própria Microsoft, pelo que não é necessário registar uma aplicação própria no Microsoft Entra.
- As bibliotecas Apache.Arrow e Apache.Arrow.Compression .NET para desserializar a resposta. A API REST de Execute DAX Queries comprime buffers Arrow com compressão de frames LZ4, pelo que
Apache.Arrow.Compressione as respetivas dependências (K4os.Compression.LZ4,K4os.Compression.LZ4.Streams,K4os.Hash.xxHash,ZstdSharp.Port) são necessárias. O passo seguinte mostra como os descarregar. - As seguintes definições de locatário estão ativadas no portal de administração do Power BI:
- API REST para executar consultas em conjuntos de dados (em Definições de programador).
- Permitir endpoints XMLA e Analisar em Excel com modelos semânticos locais (em Configurações de integração).
Instale o PowerShell 7.4 ou mais recente usando o winget:
winget install --id Microsoft.PowerShell --source winget
Após a instalação, inicie a nova shell com pwsh. Executa os comandos restantes deste tutorial dessa sessão.
Instale o módulo MicrosoftPowerBIMgmt. A -Force flag aceita o prompt do repositório não confiável Galeria do PowerShell.
Install-Module -Name MicrosoftPowerBIMgmt -Scope CurrentUser -Force
Descarregue os pacotes NuGet necessários e extraia as suas assemblagens para C:\Tools\Apache.Arrow\. Um .nupkg ficheiro é um arquivo ZIP, por isso Expand-Archive trabalha diretamente sobre ele. O loop seleciona a pasta de destino mais alta netX.0 em cada pacote para que os assemblies se mantenham compatíveis à medida que os pacotes publicam destinos mais recentes.
$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 - Autenticar
Inicie sessão no serviço Power BI de forma interativa e depois extraia um token de acesso. O Connect-PowerBIServiceAccount cmdlet não exige que registe a tua própria aplicação no Microsoft Entra.
Connect-PowerBIServiceAccount -WarningAction SilentlyContinue
$accessToken = (Get-PowerBIAccessToken).Authorization -replace '^Bearer\s+',''
2 - Criar uma solicitação com várias instruções EVALUATE
Defina os alvos do espaço de trabalho e do modelo semântico. Em seguida, construa o corpo do pedido. A query propriedade é uma única cadeia que contém três EVALUATE afirmações separadas por linhas em branco.
$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 - Enviar o pedido e capturar o fluxo de resposta bruto
Envie o pedido POST e leia o corpo da resposta como um fluxo binário. Use HttpWebRequest em vez de Invoke-RestMethod, Invoke-PowerBIRestMethod, ou Invoke-WebRequest. A resposta é um fluxo binário do protocolo Arrow IPC. Os cmdlets PowerShell de nível superior interpretam os corpos de resposta como texto, o que corrompe o conteúdo binário.
HttpWebRequest devolve o fluxo bruto sem modificações.
$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 - Analisar a resposta com vários conjuntos de resultados
O corpo da resposta é a concatenação de um fluxo IPC do Apache Arrow por cada instrução EVALUATE. O PowerShell não vem com um parser Arrow, por isso este passo carrega a Apache.Arrow biblioteca .NET através de um pequeno assistente C# inline adicionado com Add-Type. Manter a lógica de fluxo em C# mantém o site de chamada curto e devolve uma lista de conjuntos de resultados que o seu script PowerShell pode iterar. O auxiliar abre um novo ArrowStreamReader após cada marcador de fim de fluxo, pelo que o mesmo ciclo lida com qualquer número de conjuntos de resultados na resposta.
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 - Trabalhar com cada conjunto de resultados
Converte cada conjunto de resultados em PSCustomObject linhas. Agora pode encaminhar as linhas através de Where-Object, Group-Object, Export-Csv ou qualquer outro cmdlet do 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
Cada variável contém as linhas da instrução correspondente EVALUATE , na ordem em que as instruções aparecem no pedido.
Troubleshooting
-
401 Não Autorizado — O token em cache expirou. Executa
Connect-PowerBIServiceAccountnovamente para atualizar e depois relê$accessTokena partir deGet-PowerBIAccessToken. -
Avisos do MSAL durante
Connect-PowerBIServiceAccount—MicrosoftPowerBIMgmtinclui uma versão mais antiga do MSAL.NET que emite mensagens internas de rastreio (por exemplo,SetAuthorityUri,TryNormalizeRealm,MsaDeviceOperationProvider is not available) com nível de severidade de aviso. Pode ignorá-los em segurança, desde que o cmdlet apresente o blocoEnvironment/TenantId/UserName. Para os ocultar, passe-WarningAction SilentlyContinue. -
HTTP 200 com um conjunto de resultados de erro — O pedido HTTP foi bem-sucedido, mas o fluxo Arrow contém um erro. Verifique os metadados do esquema para
IsError=truee leiaFaultCodeeFaultString. Para mais detalhes, consulte as melhores práticas para a API REST Execute DAX Queries. -
Invoke-RestMethodretorna texto distorcido — Não useInvoke-RestMethod,Invoke-PowerBIRestMethod, nemInvoke-WebRequestcom esta API. A resposta é binária; UseHttpWebRequestconforme mostrado no passo 3. -
Add-Typefalha ao carregarApache.Arrow.dll— No Windows PowerShell 5.1, o pacoteApache.Arrowentra em conflito com o assemblySystem.Memoryincluído no sistema. Use o PowerShell 7.4 ou posterior. -
Não foram devolvidos conjuntos de resultados, ou foi devolvido um número de conjuntos de resultados inferior ao de instruções
EVALUATE— Confirme que cada instruçãoEVALUATEé sintaticamente válida por si só. Um único elemento inválidoEVALUATEfaz com que a API retorne um erro em vez de uma resposta parcial de múltiplos conjuntos de resultados.
Conteúdo relacionado
- Compreenda a API de Execução de Consultas DAX
- Comece com a API REST Execute DAX Queries
- Tutorial: Constrói um serviço .NET de nível intermédio com a API REST Execute DAX Queries
- Tutorial: Extração de Python de alto volume em notebooks Fabric
- Boas práticas para a API REST Execute DAX Queries
- Executar Consultas DAX Referência da API REST