Il linguaggio M: leggere i passaggi e l'editor avanzato
Power Query "scrive M per te". Saperlo leggere — e a volte ritoccarlo — è ciò che distingue chi usa Power BI da chi lo padroneggia. In questa lezione ti faccio capire la sintassi let…in, le variabili, le funzioni più usate, l'editor avanzato.
- ⏱ Tempo stimato: ~30 min + esercizi
- 🎯 Prerequisiti: L3.1, L3.2, L3.3, L3.4
- 🛠 Software: Power BI Desktop
La struttura let … in
Ogni query M ha questa forma:
let
NomeStep1 = espressione1,
NomeStep2 = espressione2 ( riferisce NomeStep1 ),
NomeStep3 = espressione3 ( riferisce NomeStep2 ),
...
UltimoStep = espressioneFinale
in
UltimoStepPunti chiave:
- let apre la lista delle variabili (gli step).
- Ogni step assegna un nome a un valore o tabella.
- Gli step sono separati da virgola (l'ultimo no).
- in indica cosa restituire (di solito l'ultimo step).
- I nomi possono contenere spazi se messi tra
#"...":#"Filtra anni".
Tipi di valore in M
I tipi base:
- number
- Numero (intero o decimale). Es. 42, 3.14.
- text
- Stringa. Es. "Mario".
- logical
- Booleano. Es. true, false.
- date / datetime / time / duration
- Date e orari. Es. #date(2026,5,26), #datetime(2026,5,26,9,0,0).
- null
- Valore mancante.
- list
- Sequenza ordinata. Es. {1, 2, 3} o {"a","b","c"}.
- record
- Coppie chiave-valore. Es. [Nome="Mario", Eta=30].
- table
- Tabella tipica di righe e colonne (quella che torna dalla maggior parte delle query).
- function
- Funzione definita dall'utente o di sistema.
- binary
- Dati binari (contenuto di file, ecc.).
Esempi pratici nelle espressioni:
// Numero
TassaIva = 0.22
// Stringa
Saluto = "Ciao " & [Cliente]
// Data letterale
Inizio = #date(2026, 1, 1)
// Lista
AreeNord = {"Lombardia","Veneto","Piemonte"}
// Record
ConfigCliente = [TipoListino="Pro", Sconto=0.10]
// Tabella inline
Festivi = #table({"Data","Festa"},
{{#date(2026,1,1), "Capodanno"},
{#date(2026,4,25), "Liberazione"}})Le famiglie di funzioni più usate
La libreria standard M ha centinaia di funzioni. Sono raggruppate per namespace:
- Table.* — tutto ciò che opera su tabelle: filtra, raggruppa, joina, trasforma colonne. Esempi:
Table.SelectRows,Table.AddColumn,Table.NestedJoin,Table.Group. - Text.* — manipolazione stringhe. Esempi:
Text.Upper,Text.Trim,Text.Contains,Text.Split. - Number.* — operazioni numeriche. Esempi:
Number.Round,Number.Abs,Number.FromText. - Date.* / DateTime.* / Time.* — operazioni temporali. Esempi:
Date.Year,Date.AddMonths,DateTime.LocalNow. - List.* — operazioni su liste. Esempi:
List.Sum,List.Contains,List.Distinct. - Record.* — operazioni su record. Esempi:
Record.Field,Record.HasFields. - Splitter.* / Combiner.* / Replacer.* — helper per split, combine, sostituzioni (usati dentro
Table.SplitColumnecc.). - Sql.*, Excel.*, Csv.*, Json.*, Web.* — connettori per ciascuna sorgente.
L'Advanced Editor in pratica
Dove: nel Power Query Editor → tab View → Advanced Editor. Vedi il codice M completo della query corrente. Puoi:
- Riordinare manualmente gli step (con cautela).
- Rinominare step.
- Aggiungere step a mano (utili per cose che il wizard non offre).
- Refactoring più complessi (es. estrarre logica in una funzione riusabile).
- Dimenticare la virgola tra gli step → "Expecting comma or end of let".
- Mettere una virgola dopo l'ultimo step → "Expecting another statement".
- Riferirsi a uno step inesistente o errore di battitura nel nome.
- Parentesi tonde/graffe sbilanciate.
Funzioni custom in M
Puoi definire una funzione assegnandola a uno step:
AssegnaFascia = (importo as number) as text =>
if importo >= 1000 then "Alto"
else if importo >= 100 then "Medio"
else "Basso",
// Uso della funzione su una colonna
ConFascia = Table.AddColumn(Tabella, "Fascia",
each AssegnaFascia([Importo]), type text)Le funzioni custom sono potenti soprattutto quando le metti come query separata (right-click sul pannello Queries → New Query → Blank Query, poi incolli il codice). Le richiami da altre query come NomeFunzione([Colonna]).
each, _ e [NomeColonna] — la sintassi che confonde
Quando vedi:
Table.SelectRows(Tabella, each [Importo] > 100)each è zucchero sintattico per "funzione anonima a 1 parametro". È equivalente a:
Table.SelectRows(Tabella, (riga) => riga[Importo] > 100)_ è il parametro implicito: dentro each, _è la riga corrente, e [NomeColonna] è il valore di quella colonna nella riga.
Table.SelectRows(Tabella, each [Importo] > 100)
Table.SelectRows(Tabella, each _[Importo] > 100)
Table.SelectRows(Tabella, (r) => r[Importo] > 100)Errori M comuni e come leggerli
Esercitazioni
Leggi una query M
Spiega cosa fa questa query, step per step:
let
Source = Csv.Document(File.Contents("C:\Dati\v.csv"), [Delimiter=";", Encoding=1252]),
Header = Table.PromoteHeaders(Source, [PromoteAllScalars=true]),
Tipo = Table.TransformColumnTypes(Header, {{"Data", type date},{"Importo", type number}}, "it-IT"),
Filtro = Table.SelectRows(Tipo, each [Data] >= #date(2025,1,1)),
Anno = Table.AddColumn(Filtro, "Anno", each Date.Year([Data]), Int64.Type)
in
AnnoRinomina gli step
Nell'Advanced Editor della query sopra, rinomina Source → CSVOrigine, Tipo → CambiaTipi, Filtro → FiltraDal2025. Ricordati di aggiornare i riferimenti nei passi successivi.
Aggiungi uno step manuale
Dopo lo step "Anno", aggiungi a mano uno step "Trimestre" che calcoli Date.QuarterOfYear([Data]) e lo metta in una colonna intera. Ricontrolla la sintassi.
Funzione custom 'AssegnaFascia'
Crea una funzione M in una query separata "fnAssegnaFascia" che prende un importo e restituisce "Alto" / "Medio" / "Basso". Poi usala in Table.AddColumn.
Sostituisci each con (r) =>
Riscrivi questa espressione usando la forma esplicita di funzione anonima:
Table.SelectRows(Tabella, each [Importo] > 100 and [Stato] = "OK")Tabella inline di Festivi
Crea una query "Festivi" come tabella inline M con 3 festività italiane (Capodanno, Liberazione 25 aprile, Ferragosto). Poi fai Merge con una tabella "Calendario" per avere una colonna "Festa" nei giorni festivi.
Refactoring: estrai pattern in funzione
Hai 5 query che fanno la stessa pulizia base (PromoteHeaders + Change Type + Trim). Come estrai questo pattern in una funzione condivisa e la richiami nelle 5?
Debug di errori M
Questa query fallisce con "Expression.SyntaxError: Token Comma expected". Trova l'errore.
let
Source = Excel.Workbook(File.Contents("vendite.xlsx"), null, true)
Foglio = Source{[Name="Sheet1"]}[Data],
Tipo = Table.TransformColumnTypes(Foglio, {{"Data", type date}})
in
TipoQuick check finale
Cosa apre 'in' in una query M?
Cos'è 'each [Importo] > 100' in M?
Hai finito il Modulo 3 — e tutto il Livello BASE ✓✓
Hai pulito i dati, scelto le modalità di connessione, capito Get Data, Gateway, OneLake e Power Query in profondità. Sei pronto per il Livello Intermedio: nel Modulo 4 entriamo nella modellazione con star schema e relazioni.