Introduzione
Panoramica
Microsoft Power Query offre una potente esperienza di acquisizione dei dati che comprende molte funzionalità. Una funzionalità chiave di Power Query è la possibilità di filtrare e combinare, ovvero di eseguire il mashup dei dati da una o più raccolte di origini dati supportate. Qualsiasi mashup di dati di questo tipo viene espresso usando il linguaggio delle formule M di Power Query, noto in modo informale come "M". Power Query incorpora documenti M in un'ampia varietà di prodotti Microsoft, tra cui Excel, Power BI, Analysis Services e Dataverse, per consentire il mashup ripetibile di dati.
In questo documento viene illustrata la specifica relativa al linguaggio M. Dopo una breve introduzione volta a creare una certa familiarità con il linguaggio, il documento presenta il linguaggio in modo più accurato attraverso vari passaggi progressivi:
La struttura lessicale definisce il set di testi lessicalmente validi.
I valori, le espressioni, gli ambienti e le variabili, gli identificatori e il modello di valutazione formano i concetti di base del linguaggio.
La specifica dettagliata dei valori, sia primitivi che strutturati, definisce il dominio di destinazione del linguaggio.
I valori si differenziano in base al tipo (a sua volta, un tipo speciale di valore), che caratterizza i principali tipi di valori e contiene metadati aggiuntivi specifici delle forme dei valori strutturati.
Il set di operatori di M definisce quali tipi di espressioni è possibile formare.
Le funzioni, un altro tipo di valori speciali, forniscono la base per un'ampia libreria standard per M e consentono l'aggiunta di nuove astrazioni.
È possibile che si verifichino errori quando si applicano operatori o funzioni durante la valutazione di un'espressione. Sebbene gli errori non siano valori, esistono metodi di gestione degli errori che eseguono il mapping inverso: dagli errori ai valori.
Le espressioni let consentono l'introduzione di definizioni ausiliarie utili per creare espressioni complesse in passaggi più brevi.
Le espressioni if supportano la valutazione condizionale.
Le sezioni forniscono un semplice meccanismo di modularità (le sezioni non vengono ancora usate da Power Query).
Una grammatica consolidata raccoglie infine i frammenti di grammatica da tutte le altre sezioni di questo documento in un'unica definizione completa.
Per i teorici dei linguaggi informatici: il linguaggio delle formule usato in questo documento è un linguaggio funzionale essenzialmente puro, di ordine superiore, tipizzato in modo dinamico e parzialmente "lazy".
Espressioni e valori
Il costrutto centrale in M è l'espressione. Un'espressione può essere valutata (calcolata), restituendo un singolo valore.
Sebbene molti valori possano essere scritti letteralmente come espressione, un valore non è un'espressione. Ad esempio, l'espressione 1
restituisce il valore 1, mentre l'espressione 1+1
restituisce il valore 2. La distinzione è sottile, ma importante. Le espressioni sono ricette per la valutazione, mentre i valori sono i risultati della valutazione.
Negli esempi seguenti vengono illustrati i diversi tipi di valori disponibili in M. Per convenzione, un valore viene scritto usando il formato letterale in cui verrebbe visualizzato in un'espressione che restituisce solo quel valore. Osservare che //
indica l'inizio di un commento che continua fino alla fine della riga.
Un valore primitivo è un valore in una sola parte, ad esempio un valore number, logical, text o null. È possibile usare un valore Null per indicare l'assenza di dati.
123 // A number true // A logical "abc" // A text null // null value
Un valore elenco è una sequenza ordinata di valori. M supporta elenchi infiniti, ma se vengono scritti come valori letterali, gli elenchi hanno una lunghezza fissa. I caratteri parentesi graffe
{
e}
indicano l'inizio e la fine di un elenco.{123, true, "A"} // list containing a number, a logical, and // a text {1, 2, 3} // list of three numbers
Un record è un set di campi. Un campo è una coppia nome/valore in cui il nome è un valore di testo univoco all'interno del record del campo. La sintassi letterale per i valori dei record consente di scrivere i nomi senza virgolette, con un formato noto anche come identificatori. Di seguito viene illustrato un record contenente tre campi denominati "
A
", "B
" e "C
", a cui sono associati i valori1
,2
e3
.[ A = 1, B = 2, C = 3 ]
Una tabella è un set di valori organizzati in colonne (identificate per nome) e righe. Non esiste una sintassi letterale per la creazione di una tabella, ma sono disponibili varie funzioni standard per la creazione di tabelle da elenchi o record.
Ad esempio:
#table( {"A", "B"}, { {1, 2}, {3, 4} } )
Viene creata una tabella con la forma seguente:
Una funzione è un valore che, se richiamato con argomenti, produce un nuovo valore. Le funzioni si scrivono elencando i parametri della funzione tra parentesi, seguiti dal simbolo vai-a
=>
, seguito dall'espressione che definisce la funzione. L'espressione si riferisce in genere ai parametri (per nome).(x, y) => (x + y) / 2`
Valutazione
Il modello di valutazione del linguaggio M è progettato sulla base del modello di valutazione comunemente presente nei fogli di calcolo, in cui l'ordine dei calcoli può essere determinato in base alle dipendenze tra le formule nelle celle.
Se sono state scritte formule in un foglio di calcolo, ad esempio Excel, è possibile riconoscere le formule a sinistra che dopo il calcolo consentiranno di ottenere i valori a destra:
In M, parti di un'espressione possono fare riferimento ad altre parti dell'espressione in base al nome e il processo di valutazione determina automaticamente l'ordine di calcolo delle espressioni a cui si fa riferimento.
È possibile usare un record per produrre un'espressione equivalente all'esempio del foglio di calcolo precedente. Quando si inizializza il valore di un campo, è possibile fare riferimento ad altri campi all'interno del record usando il nome del campo, come indicato di seguito:
[
A1 = A2 * 2,
A2 = A3 + 1,
A3 = 1
]
L'espressione precedente è equivalente alla seguente (in quanto entrambe restituiscono valori uguali):
[
A1 = 4,
A2 = 2,
A3 = 1
]
I record possono essere contenuti, ovvero annidati, in altri record. È possibile usare l'operatore di ricerca ([]
) per accedere ai campi di un record in base al nome. Il record seguente, ad esempio, include un campo denominato Sales
contenente un record e un campo denominato Total
che accede ai campi FirstHalf
e SecondHalf
del record Sales
:
[
Sales = [ FirstHalf = 1000, SecondHalf = 1100 ],
Total = Sales[FirstHalf] + Sales[SecondHalf]
]
Quando viene valutata, l'espressione precedente è equivalente alla seguente:
[
Sales = [ FirstHalf = 1000, SecondHalf = 1100 ],
Total = 2100
]
I record possono essere contenuti anche all'interno di elenchi. Per accedere a un elemento di un elenco in base al relativo indice numerico, è possibile usare l'operatore di indice posizionale ({}
). Ai valori all'interno di un elenco viene fatto riferimento usando un indice in base zero dall'inizio dell'elenco. Ad esempio, gli indici 0
e 1
vengono usati per fare riferimento al primo e al secondo elemento nell'elenco seguente:
[
Sales =
{
[
Year = 2007,
FirstHalf = 1000,
SecondHalf = 1100,
Total = FirstHalf + SecondHalf // 2100
],
[
Year = 2008,
FirstHalf = 1200,
SecondHalf = 1300,
Total = FirstHalf + SecondHalf // 2500
]
},
TotalSales = Sales{0}[Total] + Sales{1}[Total] // 4600
]
Per le espressioni di membro elenco e record (nonché per le espressioni let) viene usata la valutazione lazy, ovvero vengono valutate all'occorrenza. Per tutte le altre espressioni viene usata invece la valutazione eager, ovvero vengono valutate immediatamente quando vengono rilevate durante il processo di valutazione. Un modo efficace per tenere conto di questa differenza consiste nel ricordare che la valutazione di un'espressione elenco o record restituisce un valore di elenco o record che ricorda come devono essere calcolati gli elementi dell'elenco o i campi del record, quando richiesto (dagli operatori di ricerca o di indice).
Funzioni
In M, una funzione è un mapping da un set di valori di input a un singolo valore di output. Una funzione viene scritta specificando prima il set obbligatorio di valori di input (i parametri della funzione) e quindi un'espressione che consenta di calcolare il risultato della funzione usando i valori di input che seguono il simbolo vai a (=>
). Ad esempio:
(x) => x + 1 // function that adds one to a value
(x, y) => x + y // function that adds two values
Una funzione è un valore esattamente come un numero o un valore di testo. L'esempio seguente illustra una funzione che corrisponde al valore di un campo Add che viene quindi richiamata, o eseguita, da vari altri campi. Quando viene richiamata una funzione, viene specificato un set di valori che vengono sostituiti in modo logico per il set di valori di input obbligatorio nell'espressione del corpo della funzione.
[
Add = (x, y) => x + y,
OnePlusOne = Add(1, 1), // 2
OnePlusTwo = Add(1, 2) // 3
]
Libreria
M include un set comune di definizioni che è possibile usare nelle espressioni denominato libreria standard o, più semplicemente, libreria. Queste definizioni sono costituite da un set di valori denominati. I nomi dei valori forniti da una libreria possono essere usati in un'espressione anche se non sono stati definiti in modo esplicito dall'espressione. Ad esempio:
Number.E // Euler's number e (2.7182...)
Text.PositionOf("Hello", "ll") // 2
Operatori
M include un set di operatori che possono essere usati nelle espressioni. Gli operatori vengono applicati agli operandi per formare espressioni simboliche. Ad esempio, nell'espressione 1 + 2
i numeri 1
e 2
sono operandi e l'operatore è l'operatore di addizione (+
).
Il significato di un operatore può variare a seconda del tipo di valori degli operandi. L'operatore di addizione, ad esempio, può essere usato anche con tipi di valori diversi dai numeri:
1 + 2 // numeric addition: 3
#time(12,23,0) + #duration(0,0,2,0)
// time arithmetic: #time(12,25,0)
Un altro esempio di operatore con significato diverso a seconda dell'operando è l'operatore di combinazione (&
):
"A" & "BC" // text concatenation: "ABC"
{1} & {2, 3} // list concatenation: {1, 2, 3}
[ a = 1 ] & [ b = 2 ] // record merge: [ a = 1, b = 2 ]
Si noti che alcuni operatori non supportano tutte le combinazioni di valori. Ad esempio:
1 + "2" // error: adding number and text isn't supported
Le espressioni che, quando valutate, riscontrano condizioni di operatore non definite restituiscono errori.
Metadati UFX
I metadati sono informazioni su un valore associato a un valore. I metadati sono rappresentati come un valore di record, denominato record di metadati. I campi di un record di metadati possono essere usati per archiviare i metadati per un valore.
Ogni valore ha un record di metadati. Se il valore del record dei metadati non è stato specificato, il record dei metadati è vuoto (privo di campi).
I record di metadati consentono di associare informazioni aggiuntive a qualsiasi tipo di valore in modo non intrusivo. L'associazione di un record di metadati a un valore non comporta la modifica del valore o del relativo comportamento.
Un valore di record di metadati y
viene associato a un valore esistente x
usando la sintassi x meta y
. Il codice seguente, ad esempio, associa un record di metadati con i campi Rating
e Tags
al valore di testo "Mozart"
:
"Mozart" meta [ Rating = 5, Tags = {"Classical"} ]
Per i valori che contengono già un record di metadati non vuoto, l'applicazione di metadati ha come risultato quello di calcolare l'unione del record di metadati esistente con quello nuovo. Le due espressioni seguenti, ad esempio, sono equivalenti sia l'una all'altra sia all'espressione precedente:
("Mozart" meta [ Rating = 5 ]) meta [ Tags = {"Classical"} ]
"Mozart" meta ([ Rating = 5 ] & [ Tags = {"Classical"} ])
È possibile accedere a un record di metadati per un determinato valore usando la funzione Value.Metadata. Nell'esempio seguente l'espressione nel campo ComposerRating
accede al record di metadati del valore nel campo Composer
e quindi accede al campo Rating
del record di metadati.
[
Composer = "Mozart" meta [ Rating = 5, Tags = {"Classical"} ],
ComposerRating = Value.Metadata(Composer)[Rating] // 5
]
Espressione let
Nella maggior parte degli esempi illustrati finora, nel risultato dell'espressione erano inclusi tutti i valori letterali dell'espressione. L'espressione let
include un set di valori da calcolare, a cui assegnare nomi e da usare in un'espressione successiva che segue l'istruzione in
. Nell'esempio dei dati sulle vendite, è possibile procedere come segue:
let
Sales2007 =
[
Year = 2007,
FirstHalf = 1000,
SecondHalf = 1100,
Total = FirstHalf + SecondHalf // 2100
],
Sales2008 =
[
Year = 2008,
FirstHalf = 1200,
SecondHalf = 1300,
Total = FirstHalf + SecondHalf // 2500
]
in Sales2007[Total] + Sales2008[Total] // 4600
Il risultato dell'espressione precedente è un valore numerico (4600
) calcolato a partire dai valori associati ai nomi Sales2007
e Sales2008
.
Espressione if
L'espressione if
effettua una selezione tra due espressioni in base a una condizione logica. Ad esempio:
if 2 > 1 then
2 + 2
else
1 + 1
La prima espressione (2 + 2
) viene selezionata se l'espressione logica (2 > 1
) è true e la seconda espressione (1 + 1
) viene selezionata se è false. L'espressione selezionata (in questo caso 2 + 2
) viene valutata e diventa il risultato dell'espressione if
(4
).
Errori
Un errore indica che il processo di valutazione di un'espressione non è stato in grado di produrre un valore.
Gli errori vengono generati dagli operatori e dalle funzioni che riscontrano condizioni di errore o tramite l'espressione error. Gli errori vengono gestiti con l'espressione try
. Quando viene generato un errore, viene specificato un valore che può essere usato per indicare il motivo per cui si è verificato l'errore.
let Sales =
[
Revenue = 2000,
Units = 1000,
UnitPrice = if Units = 0 then error "No Units"
else Revenue / Units
],
UnitPrice = try Number.ToText(Sales[UnitPrice])
in "Unit Price: " &
(if UnitPrice[HasError] then UnitPrice[Error][Message]
else UnitPrice[Value])
L'esempio precedente accede al campo Sales[UnitPrice]
e formatta il valore producendo il risultato:
"Unit Price: 2"
Se il campo Units
fosse stato zero, il campo UnitPrice
avrebbe generato un errore, che sarebbe stato gestito dall'espressione try
. Il valore risultante sarebbe stato quindi:
"No Units"
Un'espressione try
converte i valori e gli errori in un valore di record che indica se l'espressione try
ha gestito o meno un errore, nonché il valore appropriato o il record di errore estratto durante la gestione dell'errore. Si consideri, ad esempio, l'espressione seguente che genera un errore e quindi lo gestisce immediatamente:
try error "negative unit count"
Questa espressione restituisce il valore di record annidato seguente, che spiega le ricerche nei campi [HasError]
, [Error]
e [Message]
nell'esempio di prezzo unitario precedente.
[
HasError = true,
Error =
[
Reason = "Expression.Error",
Message = "negative unit count",
Detail = null
]
]
Un caso comune prevede la sostituzione degli errori con i valori predefiniti. L'espressione try
può essere usata con una clausola otherwise
facoltativa per ottenere questo risultato in un formato compatto:
try error "negative unit count" otherwise 42
// 42