หมายเหตุ
การเข้าถึงหน้านี้ต้องได้รับการอนุญาต คุณสามารถลอง ลงชื่อเข้าใช้หรือเปลี่ยนไดเรกทอรีได้
การเข้าถึงหน้านี้ต้องได้รับการอนุญาต คุณสามารถลองเปลี่ยนไดเรกทอรีได้
ในบทช่วยสอนนี้ คุณใช้ PowerShell เพื่อส่งคําขอ REST API แบบสอบถาม DAX เดียวที่มี EVALUATE หลายคําสั่ง จากนั้นแยกวิเคราะห์การตอบสนองลูกศร Apache แบบหลายชุดผลลัพธ์ รูปแบบนี้ช่วยให้คุณสามารถดึงชุดผลลัพธ์ที่เกี่ยวข้องหลายชุดในการเดินทางไปกลับครั้งเดียวจากสคริปต์ระบบอัตโนมัติของ PowerShell
เหตุใดจึงต้องส่งใบแจ้งยอด EVALUATE หลายรายการในคําขอเดียว
API ดําเนินการคิวรี DAX ยอมรับสตริงเดียว query ที่สามารถมีคําสั่งได้หลาย EVALUATE คําสั่ง แต่ละคําสั่งจะส่งกลับชุดผลลัพธ์ของตัวเอง และเนื้อหาการตอบกลับคือการต่อกันของสตรีม Arrow IPC หนึ่งรายการต่อ EVALUATE คําสั่งในลําดับการประกาศ การส่งคิวรีที่เกี่ยวข้องร่วมกันจะหลีกเลี่ยงค่าใช้จ่ายต่อคําขอของการเรียก HTTP แยกต่างหาก รวมถึงการตรวจสอบโทเค็น Microsoft Entra เพิ่มเติมและการเริ่มต้นกลไกจัดการ DAX การส่งใบแจ้งยอดหลาย EVALUATE ใบในคําขอเดียวยังสามารถช่วยลดผลกระทบของการควบคุมปริมาณคําขอได้อีกด้วย Power BI จํากัดผู้เรียกไว้ที่ 120 คําขอคิวรีต่อนาทีต่อผู้ใช้สําหรับการดําเนินการคิวรีแบบจําลองความหมาย
สิ่งที่คุณสร้าง
ในสคริปต์ PowerShell หนึ่งสคริปต์ คุณ:
- รับโทเค็นการเข้าถึง Microsoft Entra
- สร้างเนื้อหาคําขอที่มี
queryคําสั่งสามEVALUATEคําสั่ง - ส่งคําขอและบันทึกสตรีมการตอบสนอง Arrow IPC ดิบ
- แยกวิเคราะห์การตอบกลับเป็นชุดผลลัพธ์หนึ่งชุดต่อ
EVALUATEคําสั่ง - แสดงแต่ละชุดผลลัพธ์เป็นวัตถุ PowerShell
ข้อกำหนดเบื้องต้น
- PowerShell 7.4 หรือใหม่กว่า Windows PowerShell 5.1 ไม่ได้รับการสนับสนุน
Apache.Arrowเนื่องจากแพคเกจที่ใช้ในบทช่วยสอนนี้ขัดแย้งกับSystem.Memoryแอสเซมบลีที่รวมอยู่ใน PowerShell 5.1 - พื้นที่ทํางาน Power BI บนความจุ Premium หรือ Fabric ที่มีแบบจําลองความหมายอย่างน้อยหนึ่งแบบ
- สิทธิ์ในการสร้างและอ่านบนแบบจําลองความหมาย
- โมดูล MicrosoftPowerBIMgmt สําหรับการรับรองความถูกต้อง cmdlet ใช้แอปไคลเอ็นต์ Power BI บุคคลที่หนึ่งของ Microsoft ดังนั้นคุณไม่จําเป็นต้องลงทะเบียนแอปของคุณเองใน Microsoft Entra
- ไลบรารี Apache.Arrow และ Apache.Arrow.Compression .NET เพื่อ deserialize การตอบสนอง ดําเนินการคิวรี DAX REST API บีบอัดบัฟเฟอร์ลูกศรด้วยการบีบอัดเฟรม LZ4 ดังนั้นจึง
Apache.Arrow.Compressionจําเป็นต้องมีการขึ้นต่อกัน (K4os.Compression.LZ4,K4os.Compression.LZ4.Streams, ,K4os.Hash.xxHash)ZstdSharp.Portขั้นตอนต่อไปจะแสดงวิธีการดาวน์โหลด - การตั้งค่าผู้เช่าต่อไปนี้ที่เปิดใช้งานในพอร์ทัลผู้ดูแลระบบ Power BI:
- ชุดข้อมูลดําเนินการคิวรี REST API (ภายใต้ การตั้งค่านักพัฒนา)
- อนุญาตตําแหน่งข้อมูล XMLA และวิเคราะห์ใน Excel ด้วยโมเดลความหมายภายในองค์กร (ภายใต้ การตั้งค่าการรวม)
ติดตั้ง PowerShell 7.4 หรือใหม่กว่าโดยใช้ winget:
winget install --id Microsoft.PowerShell --source winget
หลังการติดตั้ง ให้เปิดเชลล์ใหม่ด้วยpwsh เรียกใช้คําสั่งที่เหลือในบทช่วยสอนนี้จากเซสชันนั้น
ติดตั้งโมดูล MicrosoftPowerBIMgmt
-Forceค่าสถานะยอมรับพร้อมท์ PowerShell Gallery untrusted-repository
Install-Module -Name MicrosoftPowerBIMgmt -Scope CurrentUser -Force
ดาวน์โหลดแพ็คเกจ NuGet ที่จําเป็นและแยกแอสเซมบลีไปที่C:\Tools\Apache.Arrow\
.nupkgไฟล์เป็นไฟล์เก็บถาวร ZIP ดังนั้นจึงExpand-Archiveใช้งานได้โดยตรง ลูปจะเลือกโฟลเดอร์เป้าหมายสูงสุด netX.0 ในแต่ละแพ็คเกจ เพื่อให้แอสเซมบลีเข้ากันได้เมื่อแพ็คเกจเผยแพร่เป้าหมายที่ใหม่กว่า
$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 - ตรวจสอบสิทธิ์
ลงชื่อเข้าใช้บริการของ บริการของ Power BI แบบโต้ตอบ จากนั้นแยกโทเค็นการเข้าถึง
Connect-PowerBIServiceAccount cmdlet ไม่ต้องการให้คุณลงทะเบียนแอปของคุณเองใน Microsoft Entra
Connect-PowerBIServiceAccount -WarningAction SilentlyContinue
$accessToken = (Get-PowerBIAccessToken).Authorization -replace '^Bearer\s+',''
2 - สร้างคําขอด้วยคําสั่ง EVALUATE หลายคําสั่ง
กําหนดเป้าหมายพื้นที่ทํางานและแบบจําลองความหมาย จากนั้นสร้างเนื้อหาคําขอ คุณสมบัติเป็น query สตริงเดียวที่มีคําสั่งสาม EVALUATE คําสั่งคั่นด้วยบรรทัดว่าง
$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 - ส่งคําขอและบันทึกสตรีมการตอบกลับดิบ
ส่งคําขอ POST และอ่านเนื้อหาการตอบกลับเป็นสตรีมไบนารี ใช้ HttpWebRequest แทน Invoke-RestMethod, Invoke-PowerBIRestMethod, หรือ Invoke-WebRequest. การตอบสนองคือสตรีม IPC ลูกศรไบนารี cmdlet PowerShell ระดับสูงจะตีความเนื้อหาการตอบสนองเป็นข้อความ ซึ่งทําให้เนื้อหาไบนารีเสียหาย
HttpWebRequest ส่งคืนสตรีมดิบที่ไม่ได้แก้ไข
$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 - แยกวิเคราะห์การตอบสนองหลายชุดผลลัพธ์
เนื้อหาการตอบสนองคือการต่อสตรีม Apache Arrow IPC หนึ่ง EVALUATE สตรีมต่อคําสั่ง PowerShell ไม่ได้มาพร้อมกับตัวแยกวิเคราะห์ลูกศร ดังนั้นขั้นตอนนี้จะโหลดApache.Arrowไลบรารี .NET ผ่านตัวช่วย C# แบบอินไลน์ขนาดเล็กที่เพิ่มด้วยAdd-Type การเก็บตรรกะสตรีมลูปไว้ใน C# จะทําให้ไซต์การโทรสั้นและส่งคืนรายการชุดผลลัพธ์ที่สคริปต์ PowerShell ของคุณสามารถทําซ้ําได้ ตัวช่วยจะเปิด ArrowStreamReader เครื่องหมายใหม่หลังจากจุดสิ้นสุดสตรีมแต่ละรายการ ดังนั้นลูปเดียวกันจะจัดการชุดผลลัพธ์จํานวนเท่าใดก็ได้ในการตอบกลับ
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 - ทํางานกับแต่ละชุดผลลัพธ์
แปลงชุดผลลัพธ์แต่ละชุดเป็น PSCustomObject แถว ตอนนี้คุณสามารถไปป์แถวผ่าน Where-Object, Group-Object, หรือ Export-Csvcmdlet 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
ตัวแปรแต่ละตัวจะเก็บแถวจากคําสั่งที่ EVALUATE สอดคล้องกันตามลําดับที่คําสั่งปรากฏในคําขอ
การแก้ไขปัญหา
-
401 ไม่ได้รับอนุญาต — โทเค็นที่แคชไว้หมดอายุ เรียกใช้
Connect-PowerBIServiceAccountอีกครั้งเพื่อรีเฟรช แล้วอ่าน$accessTokenใหม่จากGet-PowerBIAccessToken -
คําเตือน MSAL ระหว่าง
Connect-PowerBIServiceAccount—MicrosoftPowerBIMgmtรวม MSAL.NET รุ่นเก่าที่ส่งข้อความติดตามภายใน (เช่นSetAuthorityUri, ,TryNormalizeRealm)MsaDeviceOperationProvider is not availableที่ระดับความรุนแรงของคําเตือน พวกเขาปลอดภัยที่จะเพิกเฉยตราบใดที่ cmdlet พิมพ์EnvironmentUserName/TenantId/ บล็อก หากต้องการระงับ ให้ผ่าน-WarningAction SilentlyContinue -
HTTP 200 ที่มีชุดผลลัพธ์ข้อผิดพลาด — คําขอ HTTP สําเร็จ แต่สตรีมลูกศรมีข้อผิดพลาด ตรวจสอบข้อมูลเมตาของ Schema สําหรับ
IsError=trueและอ่านFaultCodeและFaultStringสําหรับรายละเอียด โปรดดู แนวทางปฏิบัติที่ดีที่สุดสําหรับ REST API ของการดําเนินการคิวรี DAX -
Invoke-RestMethodส่งคืนข้อความที่อ่านไม่ออก — อย่าใช้Invoke-RestMethod,Invoke-PowerBIRestMethodหรือInvoke-WebRequestกับ API นี้ คําตอบเป็นไบนารี ใช้HttpWebRequestตามที่แสดงในขั้นตอนที่ 3 -
Add-TypeโหลดApache.Arrow.dllไม่สําเร็จ — บน Windows PowerShell 5.1 แพคเกจขัดแย้งApache.Arrowกับแอสเซมบลีในกล่องSystem.Memoryใช้ PowerShell 7.4 หรือใหม่กว่า -
ไม่มีหรือน้อยกว่าชุดผลลัพธ์ที่ส่งคืนมากกว่า
EVALUATEคําสั่ง — ยืนยันว่าแต่ละEVALUATEคําสั่งถูกต้องตามหลักไวยากรณ์ด้วยตัวเอง การไม่ถูกต้องEVALUATEเพียงครั้งเดียวทําให้ API แสดงข้อผิดพลาดแทนที่จะเป็นการตอบสนองหลายชุดผลลัพธ์บางส่วน
เนื้อหาที่เกี่ยวข้อง
- ทําความเข้าใจ API คิวรี DAX
- เริ่มต้นใช้งาน REST API ดําเนินการคิวรี DAX
- บทช่วยสอน: สร้างบริการระดับกลาง.NET ด้วย Execute DAX Queries REST API
- บทช่วยสอน: การแยก Python ปริมาณมากในสมุดบันทึก Fabric
- แนวทางปฏิบัติที่ดีที่สุดสําหรับ REST API ของการดําเนินการคิวรี DAX
- ดําเนินการอ้างอิง REST API แบบสอบถาม DAX