Zum Hauptinhalt wechseln

Funktionen und Beispielabfragen von Power Query

Kombinieren Sie die Funktionen des Cognite Data Fusion (REST API) connector for Power BI mit Power Query, um mithilfe der Cognite API Daten abzurufen und zu transformieren und mithilfe von Microsoft Power BI und Microsoft Excel Berichte und Dashboards zu erstellen.

Beta

Die in diesem Abschnitt beschriebenen Funktionen befinden sich für ausgewählte Kunden im Beta-Status und können noch geändert werden.

Sie können die auf dieser Seite aufgeführten Abfragen und Power Query-Funktionen kopieren und an die Anforderungen Ihres Unternehmens anpassen.

Hilfsfunktionen

Zeitstempel von/nach Epoch konvertieren

CDF-Ressourcentypen erwarten Zeitstempel unter Angabe der Millisekunden seit Unix-Epoch und geben ebensolche Zeitstempel zurück. Power Query verfügt über keine Methoden, mit denen dieses Format in einen datetimezone-Typ geparst werden könnte, um zeitzonenbasierte Zeitstempel darzustellen. CDF-Datenmodelle stellen Zeitstempel mithilfe des ISO 8601-Formats für primitive Felder dar.

Verwenden Sie die unten aufgeführten Funktionen, um zwischen einer datetimezone-Variablen und Millisekunden seit Unix-Epoch und von einer datetimezoneVariablen zu Text im ISO 8601-Format zu konvertieren.

ConvertDateTimeZoneToMs

Convert datetimezone to milliseconds since epoch
//
(dtz as datetimezone) as number =>
let
// Convert the input DateTimeZone to UTC
UtcDateTime = DateTimeZone.RemoveZone(DateTimeZone.SwitchZone(dtz, 0)),
// Define the Unix epoch start
UnixEpochStart = #datetime(1970, 1, 1, 0, 0, 0),
// Calculate the duration between the input date and Unix epoch start
Delta = UtcDateTime - UnixEpochStart,
// Convert duration to total milliseconds
TotalMilliseconds = Duration.TotalSeconds(Delta) * 1000
in
TotalMilliseconds

ConvertMsToDateTimeZone

Convert milliseconds since epoch to datetimezone
(ms as number) as datetimezone =>
let
// Convert ms to seconds
SecondsSinceEpoch = ms / 1000,

// Create a duration
DurationSinceEpoch = #duration(0, 0, 0, SecondsSinceEpoch),

// Add duration to Unix epoch start to get UTC datetime
UnixEpochStart = #datetime(1970, 1, 1, 0, 0, 0),
UtcDateTime = UnixEpochStart + DurationSinceEpoch,

// Convert UTC datetime to local time zone
LocalDateTimeZone = DateTimeZone.From(UtcDateTime)
in
LocalDateTimeZone

ConvertDateTimeZoneToIso

Convert DateTimeZone to ISO 8601 text representation
(dtz as datetimezone) as text =>
let
// Use DateTimeZone.ToText with ISO 8601 format
Result = DateTimeZone.ToText(dtz, [Format="yyyy-MM-ddTHH:mm:sszzz", Culture="en-US"])
in
Result

Funktionen hinzufügen

Um in Power Query eine neue Funktion hinzuzufügen, wählen Sie Get Data > Blank Query aus und geben Sie Ihre Funktion ein oder kopieren Sie eine der unten aufgeführten Funktionen.

Zeitdeltas

Üblicherweise werden Start- und End-Zeitstempel basierend auf Zeitdeltas festgelegt. Die Werte werden aktualisiert, sobald ein Datensatz aktualisiert wird. Im Beispiel unten ist EndTime die aktuelle Zeit und StartTime liegt 7 Tage vor EndTime. Sie können das Beispiel anpassen, um andere Zeitdeltas zu verwenden.

Time deltas
CurrentTime = DateTimeZone.LocalNow(),
EndTime = CurrentTime,
StartTime = CurrentTime - #duration(7, 0, 0, 0)

Allgemeine GET-Anfrage

Wenn Sie Daten mithilfe der GetCDF-Funktion des CDF REST API connector for Power BI aus CDF abrufen, müssen Sie Abfrageparameter verwenden, um Filter zur Auswahl der abzurufenden Daten weiterzugeben.

Das Beispiel unten zeigt, wie Sie die Abfrageparameter externalIdPrefix und limit des Endpunkts /timeseries an die URL anfügen können, um Daten serverseitig zu filtern.

List all time series instances with an externalId starting with a specific string
let
Source = GetCDF("/timeseries?externalIdPrefix=EVE&limit=1000")
in
Source

Allgemeine POST-Anfrage

Wenn Sie Daten mithilfe der PostCDF-Funktion aus CDF abrufen, müssen Sie einen Anfragekörper schreiben, um die abzurufenden Daten auszuwählen. Die Funktion akzeptiert eine Textdarstellung des JSON-Körpers. Sie können den Körper aber auch mithilfe eines Power Query-Datensatzdatentyps schreiben und dann in einen JSON-Textdatentyp umwandeln, bevor Sie den Wert an die PostCDF-Funktion übergeben.

List all data modeling instances for a view using the DMS API
let
SpaceExternalId = "Geography",
ViewExternalId = "City",
ViewVersion = "1",
Body = [
sources = {
[
source = [
type = "view",
space = SpaceExternalId,
externalId = ViewExternalId,
version = ViewVersion
]
]
},
limit = 1000
],
BodyText = Text.FromBinary(Json.FromValue(Body)),
Source = PostCDF("/models/instances/list", BodyText)
in
Source

Alternativ können Sie den POST-Körper manuell als Text verfassen, müssen dabei aber doppelte Anführungszeichen (") durch andere Doppelanführungszeichen maskieren:

let
BodyText = "{""sources"": [{""source"": {""type"": ""view"", ""space"": ""Geography"", ""externalId"": ""City"", ""version"": ""1""}}], ""limit"": 1000}",
Source = PostCDF("/models/instances/list", BodyText)
in
Source

Wenn Sie eine POST-Anfrage wiederholt verwenden möchten, können Sie sie in eine Power Query-Funktion umwandeln. Zum Beispiel:

(SpaceExternalId as text, ViewExternalId as text, ViewVersion as text) as table =>
let
Body = [
sources = {
[
source = [
type = "view",
space = SpaceExternalId,
externalId = ViewExternalId,
version = ViewVersion
]
]
},
limit = 1000
],
BodyText = Text.FromBinary(Json.FromValue(Body)),
Source = PostCDF("/models/instances/list", BodyText)
in
Source

Zur Festlegung des Funktionsnamens können Sie im Power Query-Editor mit der rechten Maustaste auf den Eintrag in der Abfrageliste klicken und dann Rename auswählen.

Wenn die obige Funktion ListInstancesDMS genannt wird, können Sie sie in einer neuen Abfrage verwenden, indem Sie die Feldwerte in Power Query eingeben oder eine neue Abfrage schreiben:

let
Source = ListInstancesDMS("Geography", "City", "1")
in
Source

GraphQL-Anfragen

Wenn Sie Daten mithilfe der GraphQL-Funktion aus CDF abrufen, müssen Sie eine GraphQL-Anfrage schreiben, um die aus einem bestimmten Datenmodell abzurufenden Daten auszuwählen. Die Funktion erwartet von Ihnen, dass Sie die externe ID von space, die externe ID von view, die Version von view, die auszuführende GraphQL-Abfrage und optional einen Satz Variablen für die Verwendung in der Abfrage angeben.

Die unten dargestellte Abfrage verwendet die GraphQL-Syntax und übergibt die Variablen als JSON-Texte. Die Verwendung von Variablen in der Abfrage vereinfacht die Parametrisierung und den Einsatz in Zusammenhang mit externen Werten.

List all work orders with an end date greater than a specific date
let
Source = GraphQL(
"cdf_idm",
"CogniteProcessIndustries",
"v1",
"query MyQuery($cursor: String, $endTime: Timestamp) {#(lf) listCogniteMaintenanceOrder(#(lf) first: 1000#(lf) after: $cursor#(lf) filter: {endTime: {gte: $endTime}}#(lf) ) {#(lf) items {#(lf) name#(lf) type#(lf) startTime#(lf) endTime#(lf) priority#(lf) }#(lf) pageInfo {#(lf) endCursor#(lf) hasNextPage#(lf) }#(lf) }#(lf)}",
"{""endTime"": ""2024-10-01T00:00:00+02:00""}"
)
in
Source

Die von Power Query verwendete M-Sprache unterstützt derzeit keine mehrzeiligen Strings, weshalb die Anfrage in einer einzigen Zeile erscheinen muss. #(lf) ist das Zeichen für den Zeilenumbruch. Im obigen Beispiel wurde die Abfrage in das Textfeld in Power Query eingefügt und die Variablen wurden als JSON-Text übergeben. Beachten Sie, wie Power BI Zeilenumbrüche in die Originalabfrage eingefügt und die Abfrage als einzeilige Textvariable ausgedrückt hat.

Alternativ können Sie die Abfrage auch in Form von mehreren einzeiligen Texten verfassen und anschließend die Text.Combine-Funktion verwenden, um Zeilenumbrüche zur Abfrage hinzuzufügen. Sie können die Variablen als Power Query-Datensätze definieren und vor der Übergabe an die GraphQL-Funktion in JSON-Text umwandeln. Schauen Sie sich beispielsweise an, wie die ConvertDateTimeZoneToIso-Funktion eine datetimezone-Variable in eine Textdarstellung im ISO 8601-Format umwandelt und dann als Variable an die Abfrage übergibt.

let
// This could be a parameter or referenced from another query
EndTime = #datetimezone(2024, 10, 1, 0, 0, 0, 2, 0),
VariablesRecord = [
endTime = ConvertDateTimeZoneToIso(EndTime)
],
VariablesText = Text.FromBinary(Json.FromValue(VariablesRecord)),
Query = Text.Combine({
"query MyQuery($cursor: String, $endTime: Timestamp) {",
" listCogniteMaintenanceOrder(",
" first: 1000",
" after: $cursor",
" filter: {endTime: {gte: $endTime}}",
" ) {",
" items {",
" name",
" type",
" startTime",
" endTime",
" priority",
" }",
" pageInfo {",
" endCursor",
" hasNextPage",
" }",
" }",
"}"
}, "#(lf)"),
Data = GraphQL(
"cdf_idm",
"CogniteProcessIndustries",
"v1",
Query,
VariablesText
)
in
Data

Komplexere Beispiele

Je nach Form der Cognite-API-Antwort benötigen Sie möglicherweise weitere Power Query-Transformationen für den Abruf und die Transformation der Daten. Sie können die auf dieser Seite aufgeführten Beispiele kopieren und an die Anforderungen Ihres Unternehmens anpassen.

Sequenzzeilen mit der PostCDF-Funktion abrufen

Die unten aufgeführte Power Query-Funktion dient zum Abruf und zur Verarbeitung von Sequenzzeilendaten einer externen Sequenz-ID aus CDF. Sie sendet eine POST-Anfrage, extrahiert Spalteninformationen, erweitert verschachtelte Antwortdaten und ordnet sie in einem tabellarischen Format an. Die Funktion sorgt für die Datentypumwandlung, beseitigt nicht benötigte Felder und gruppiert die Daten nach Zeilen. Die finale Ausgabe ist eine gut strukturierte Tabelle mit korrekt formatierten Spalten.

Fetch sequence rows with PostCDF
(externalId as text) as table =>
let
RequestBody = "{""externalId"": """ & externalId & """, ""limit"": 10000}",
Response = PostCDF("/sequences/data/list", RequestBody),
// Extract columns information from the first page
FirstPage = Response{0},
Columns = FirstPage[columns],
ColumnNames = List.Transform(Columns, each [externalId]),
ColumnTypes = List.Transform(Columns, each
if [valueType] = "STRING" then type text else
if [valueType] = "DOUBLE" then type number else
if [valueType] = "LONG" then Int64.Type
else type any
),
// Extract the 'values' from each row
Rows = Table.ExpandListColumn(Response, "rows"),
ValuesTable = Table.ExpandRecordColumn(Rows, "rows", {"rowNumber", "values"}, {"rows.rowNumber", "rows.values"}),
RemoveColumns = Table.RemoveColumns(ValuesTable,{"id", "externalId", "columns", "nextCursor"}),
ExpandValues = Table.ExpandListColumn(RemoveColumns, "rows.values"),
// Group by rowNumber and create a record for each row
GroupedRows = Table.Group(ExpandValues, {"rows.rowNumber"}, {
{"RowData", (t) => Record.FromList(t[rows.values], ColumnNames)}
}),
// Expand the RowData column
ExpandRows = Table.ExpandRecordColumn(GroupedRows, "RowData", ColumnNames),
// Set column data types
FinalTable = Table.TransformColumnTypes(ExpandRows, List.Zip({ColumnNames, ColumnTypes}))
in
FinalTable

Um die Funktion zu verwenden:

let
Source = RetrieveSequenceRows("sequence-externalId")
in
Source

Abruf von Instanzen aus dem DMS-Abfrageendpunkt mit der PostCDF-Funktion

Die unten dargestellte Power Query-Funktion sorgt für den Abruf und die Verarbeitung von Datenmodellierungsinstanzen für eine DMS-Abfrage. Sie durchblättert die Antwort, extrahiert die Instanzen und erweitert die verschachtelten Daten.

Fetch instances from the DMS query endpoint with PostCDF
(query as text) as table =>
let
FetchPage = (query as text, optional cursors as nullable record) as table =>
let
Query = Json.Document(query),
UpdatedQuery =
if cursors <> null then
let
// Get all field names of both records
QueryWithFields = Record.FieldNames(Query[with]),
QUerySelectFields = Record.FieldNames(Query[select]),
CursorsFields = Record.FieldNames(cursors),
// Find the intersection of field names
CommonFields = List.Intersect({QueryWithFields, QUerySelectFields, CursorsFields}),
// Create new records containing only the common fields
UpdatedQueryWithAndSelect = Record.TransformFields(
Query,
{
{"with", each Record.SelectFields(_, CommonFields)},
{"select", each Record.SelectFields(_, CommonFields)}
}
)
in
UpdatedQueryWithAndSelect
else
Query,
// Add cursors if they are provided
UpdatedQueryWithCursors =
if cursors <> null then
Record.AddField(UpdatedQuery, "cursors", cursors)
else
UpdatedQuery,
FinalBody = Text.FromBinary(Json.FromValue(UpdatedQueryWithCursors)),
Response = PostCDF("/models/instances/query", FinalBody)
in
Response,
// Helper function to create next cursor record from result table
CreateNextCursorRecordFromTable = (inputTable as table) as record =>
let
RecordsList = List.Transform(
Table.ToRecords(inputTable), each Record.FromList({[nextCursor]}, {[resultExpression]})
),
CombinedRecord = Record.Combine(RecordsList)
in
CombinedRecord,
// Helper function to check if all cursors are null
AllCursorsNull = (cursorsRecord as record) as logical =>
let
CursorValues = Record.ToList(cursorsRecord),
NullCount = List.Count(List.Select(CursorValues, each _ = null))
in
NullCount = List.Count(CursorValues),
// Helper function to aggregate items from all pages and convert to tables
AggregateResults = (results as list) as table =>
let
// Combine all tables
CombinedTable = Table.Combine(results),
// Group by resultExpression and convert items to tables
GroupedTable = Table.Group(
CombinedTable,
{"resultExpression"},
{
{
"items",
each
Table.FromRecords(
List.Combine(List.Transform([items], each if Value.Is(_, type list) then _ else {
_
}))
),
type table
}
}
)
in
GroupedTable,
// Main pagination logic
FetchAllPages = () as list =>
let
// Initialize accumulator
InitialAcc = [
results = {},
currentCursors = null,
hasMore = true
],
// Pagination function
PaginationFunction = (acc as record) =>
let
CurrentPage = FetchPage(query, acc[currentCursors]),
NextCursors = CreateNextCursorRecordFromTable(CurrentPage),
HasMoreResults = not AllCursorsNull(NextCursors) and Table.RowCount(CurrentPage) > 0,
UpdatedResults = List.Combine({acc[results], {CurrentPage}})
in
[
results = UpdatedResults,
currentCursors = NextCursors,
hasMore = HasMoreResults
],
// Keep fetching until no more results
AllResults = List.Generate(
() => InitialAcc, each _[hasMore], each PaginationFunction(_), each _[results]
),
// Get the final list of results
FinalResults = List.Last(AllResults)
in
FinalResults,
// Execute pagination and combine results
AllPages = FetchAllPages(),
FinalTable = AggregateResults(AllPages)
in
FinalTable

Um die Funktion zu verwenden:

let
Query = [
with = [
cities = [
nodes = [
filter = [
hasData = {
[
space = "Geography",
externalId = "City",
version = "1",
#"type" = "view"
]
}
],
chainTo = "destination",
direction = "outwards"
]
],
countries = [
nodes = [
filter = [
hasData = {
[
space = "Geography",
externalId = "Country",
version = "1",
#"type" = "view"
]
}
],
chainTo = "destination",
direction = "outwards"
]
]
],
select = [
cities = [
sources = {
[
source = [
space = "Geography",
externalId = "City",
version = "1",
#"type" = "view"
],
properties = {
"name"
}
]
}
],
countries = [
sources = {
[
source = [
space = "Geography",
externalId = "Country",
version = "1",
#"type" = "view"
],
properties = {
"name"
}
]
}
]
]
],
QueryText = Text.FromBinary(Json.FromValue(Query)),
Source = QueryDMS(QueryText)
in
Source
Tipp

Fügen Sie Filter wie den hasData-Filter im obigen Beispiel hinzu, um zu vermeiden, dass alle Instanzen von CDF abgerufen werden.

Abruf von Zeitreihendatenpunkten mit der PostCDF-Funktion

Die unten dargestellte Power Query-Funktion sorgt für den Abruf und die Verarbeitung von aggregierten Zeitreihendatenpunkten für eine Zeitreihe innerhalb eines Zeitbereichs. Sie wandelt für die CDF-API-Anfragen lokale Zeitzoneneingaben in UTC um, unterstützt mehrere Aggregate und eine angepasste Granularität und kümmert sich um die Paginierung der Daten. Anschließend wandelt die Funktion die zurückgegebenen UTC-Zeitstempel zurück in die lokale Zeitzone, erweitert die verschachtelte API-Antwort und gibt eine gut strukturierte Tabelle mit korrekt formatierten Spalten aus. Darüber hinaus schließt sie lokale Zeitstempel und dezimale Aggregatwerte ein.

Das Beispiel verwendet die ConvertDateTimeZoneToMs- und ConvertMsToDateTimeZone-Funktion für die Umwandlung der Zeitstempel.

(
item as record,
start as datetimezone,
optional end as nullable datetimezone,
optional aggregates as nullable text,
optional granularity as nullable text,
optional targetUnit as nullable text,
optional targetUnitSystem as nullable text,
optional timeZone as nullable text
) =>
let
// Function to detect query type based on item record structure
DetectQueryType = (item as record) =>
let
Fields = Record.FieldNames(item),
HasId = List.Contains(Fields, "id"),
HasExternalId = List.Contains(Fields, "externalId"),
HasSpace = List.Contains(Fields, "space"),
FieldCount = List.Count(Fields),
QueryType =
if HasId and not HasExternalId and not HasSpace and FieldCount = 1 then
"id"
else if HasExternalId and not HasId and not HasSpace and FieldCount = 1 then
"externalId"
else if HasExternalId and HasSpace and not HasId and FieldCount = 2 then
"instanceId"
else
Error.Record(
"Invalid item content", "The item record does not match any supported query type", item
)
in
QueryType,
// Detect query type
queryType = DetectQueryType(item),
// Determine limit based on presence of aggregates
limit = if aggregates <> null then 10000 else 100000,
// Convert aggregates from comma-separated string to list format accepted by the API
AggregatesList = Text.Split(aggregates, ","),
AggregatesTrimmedList = List.Transform(AggregatesList, each Text.Trim(_)),
StartMs = Number.Round(ConvertDateTimeZoneToMs(start)),
EndMs = Number.Round(ConvertDateTimeZoneToMs(end)),
// Function to fetch a single page of data
FetchPage = (cursor as nullable text) =>
let
// Build body item
bodyItem =
if queryType = "id" then
[id = Record.Field(item, "id")]
& (if targetUnit <> null then [targetUnit = targetUnit] else [])
& (if targetUnitSystem <> null then [targetUnitSystem = targetUnitSystem] else [])
& (if cursor <> null then [cursor = cursor] else [])
else if queryType = "externalId" then
[externalId = Record.Field(item, "externalId")]
& (if targetUnit <> null then [targetUnit = targetUnit] else [])
& (if targetUnitSystem <> null then [targetUnitSystem = targetUnitSystem] else [])
& (if cursor <> null then [cursor = cursor] else [])
else if queryType = "instanceId" then
[ instanceId = [ space = Record.Field(item, "space"), externalId = Record.Field(item, "externalId") ] ]
& (if targetUnit <> null then [targetUnit = targetUnit] else [])
& (if targetUnitSystem <> null then [targetUnitSystem = targetUnitSystem] else [])
& (if cursor <> null then [cursor = cursor] else [])
else
error "Invalid query type",
// Build request body
body = [
items = {bodyItem},
limit = limit,
ignoreUnknownIds = true,
start = Text.From(StartMs)
]
& (if end <> null then [end = Text.From(EndMs)] else [])
& (if aggregates <> null then [aggregates = AggregatesTrimmedList] else [])
& (if granularity <> null then [granularity = granularity] else [])
& (if timeZone <> null then [timeZone = timeZone] else []),
Response = PostCDF("/timeseries/data/list", Text.FromBinary(Json.FromValue(body))),
// Try to fetch the cursor from the first item in the response
FirstItem =
if Type.Is(Value.Type(Response), type table) and Table.RowCount(Response) > 0 then
Table.First(Response)
else
null,
NextCursor = if FirstItem <> null then Record.FieldOrDefault(FirstItem, "nextCursor", null) else null,
// Handles empty response and extracts data points when present
FinalItemsList =
if Table.HasColumns(Response, "datapoints") then
let
// Clean up the response table
ColumnsToRemove = {"nextCursor", "isStep", "unit"},
ColumnsPresent = List.Intersect({Table.ColumnNames(Response), ColumnsToRemove}),
CleanedTable = Table.RemoveColumns(Response, ColumnsPresent),
// Expand the "datapoints" column
ExpandedDatapointsList = Table.ExpandListColumn(CleanedTable, "datapoints"),
// Handles the case where the list of "datapoints" is empty
FinalDataPointsList =
if List.NonNullCount(ExpandedDatapointsList[datapoints]) > 0 then
let
// Extract a sample record to determine available fields dynamically
SampleRecord = ExpandedDatapointsList[datapoints]{0},
AvailableFields = Record.FieldNames(SampleRecord),
// Expand the "datapoints" records using the available fields
ExpandedDatapointsRecords = Table.ExpandRecordColumn(
ExpandedDatapointsList, "datapoints", AvailableFields, AvailableFields
),
DataPointsList = Table.ToRecords(ExpandedDatapointsRecords)
in
DataPointsList
else
{}
in
FinalDataPointsList
else
Table.ToRecords(Response)
in
{FinalItemsList, NextCursor},
// Recursive function to accumulate all pages of data
AccumulateData = (cursor as nullable text, accumulatedItems as list) =>
let
CurrentPage = FetchPage(cursor),
NewItems = CurrentPage{0},
NextCursor = CurrentPage{1},
UpdatedAccumulatedItems = accumulatedItems & NewItems,
Result =
if NextCursor <> null then
@AccumulateData(NextCursor, UpdatedAccumulatedItems)
else
UpdatedAccumulatedItems
in
Result,
// Fetch all data
AllItems = AccumulateData(null, {}),
// Convert the accumulated items to a table
ConvertToTable =
if List.IsEmpty(AllItems) then
Table.FromList({}, Splitter.SplitByNothing(), null, null, ExtraValues.Error)
else
Table.FromList(AllItems, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
// Expand the table column and convert timestamps
ExpandedTable =
if not Table.IsEmpty(ConvertToTable) and Table.HasColumns(ConvertToTable, "Column1") then
let
TmpTable = Table.ExpandRecordColumn(
ConvertToTable, "Column1", Record.FieldNames(ConvertToTable{0}[Column1])
),
// timestamp should be always present when there are datapoints
FixType = Table.TransformColumnTypes(TmpTable, {{"timestamp", Int64.Type}}),
ParseTimestamp = Table.TransformColumns(FixType, {"timestamp", each ConvertMsToDateTimeZone(_)}),
ParsedWithType = Table.TransformColumnTypes(ParseTimestamp, {{"timestamp", type datetimezone}}),
// check if the timeseries is of type string
FirstEntry = ParsedWithType{0},
IsString = FirstEntry[isString],
CleanedTable = Table.RemoveColumns(ParsedWithType, {"isString"}),
// Convert aggregate/value columns to decimal number
ValuesAsDecimal =
if aggregates <> null then
Table.TransformColumnTypes(
CleanedTable, List.Transform(AggregatesTrimmedList, each {_, type number})
)
else if IsString then
CleanedTable
else
Table.TransformColumnTypes(
CleanedTable, List.Transform({"value"}, each {_, type number})
),
// Check if "id" column is present and convert to integer
IdAsInteger =
if Table.HasColumns(ValuesAsDecimal, "id") then
Table.TransformColumnTypes(ValuesAsDecimal, {{"id", Int64.Type}})
else
ValuesAsDecimal
in
IdAsInteger
else
ConvertToTable
in
ExpandedTable

Die Funktion ist komplexer als die vorherigen Beispiele und bewältigt viele verschiedene Szenarien wie Paginierung, Datentypumwandlung und die Erweiterung verschachtelter Daten. Um die Funktion zu verwenden:

let
Source = RetrieveDataPoints(
[ externalId = "EVE-TI-FORNEBU-01-3" ],
#datetimezone(2024, 10, 1, 0, 0, 0, 2, 0),
#datetimezone(2024, 10, 13, 10, 0, 0, 2, 0),
"average,max,min",
"1d",
null,
"SI",
"Europe/Oslo"
)
in
Source

Auf Basis dieser Funktion können Sie eine weitere Funktion erstellen, um eine Liste von externen Zeitreihen-IDs zu durchlaufen und die Ergebnisse in einer großen Tabelle zusammenzufassen. Bei der Liste kann es sich um eine Spalte einer anderen Tabelle handeln, in der Sie z. B. Zeitreihen filtern. Sie können die Funktion anpassen, um eine Liste von internen IDs oder von Instanz-IDs zu durchlaufen.

(
externalIds as list,
start as datetimezone,
end as datetimezone,
aggregates as text,
granularity as text,
optional targetUnitSystem as nullable text,
optional timeZone as nullable text
) =>
let
// Iterate over each externalId and get corresponding table
TablesList = List.Transform(
externalIds,
each RetrieveDataPoints(
[ externalId = _ ],
start,
end,
aggregates,
granularity,
null,
targetUnitSystem,
timeZone
)
),
// Combine all tables into one
CombinedTable = Table.Combine(TablesList)
in
CombinedTable

Um die Funktion zu verwenden:

let
Source = RetrieveDataPointsMultipleTs(
{"EVE-TI-FORNEBU-01-2", "EVE-TI-FORNEBU-01-3"},
#datetimezone(2024, 10, 1, 0, 0, 0, 2, 0),
#datetimezone(2024, 10, 13, 10, 0, 0, 2, 0),
"average,max,min",
"1d",
"SI",
"Europe/Oslo"
)
in
Source
Weitere Informationen