Perché CALCULATE è la funzione centrale del DAX

In Power BI ogni misura viene valutata dentro un filter context: l’insieme di filtri attivi determinati da righe, colonne, slicer e visual in cui la misura compare. CALCULATE è l’unica funzione che permette di modificare quel filter context prima di eseguire l’aggregazione. La sintassi è CALCULATE(<espressione>, <filtro1>, <filtro2>, …): ogni argomento filtro viene applicato al contesto esistente e poi l’espressione viene ricalcolata.

Capire come i filtri interagiscono con quelli già presenti è il cuore dell’esame: alcuni modificatori sostituiscono un filtro, altri lo aggiungono, altri lo rimuovono.

I modificatori più comuni

  • FILTER: restituisce una tabella filtrata riga per riga; utile quando la condizione è complessa o coinvolge una misura (CALCULATE([Vendite], FILTER(Prodotti, Prodotti[Prezzo] > 100))). Attenzione: FILTER itera, quindi è più costoso di un semplice predicato di colonna.
  • ALL: rimuove i filtri da una tabella o da colonne specifiche. È lo strumento tipico per i denominatori: ALL(Prodotti) ignora ogni selezione sui prodotti.
  • REMOVEFILTERS: equivalente semantico di ALL usato come modificatore, ma più leggibile perché dichiara l’intento (“rimuovi i filtri”) invece di “restituisci tutte le righe”.
  • ALLEXCEPT: rimuove i filtri da tutte le colonne di una tabella tranne quelle elencate. Perfetto per mantenere il raggruppamento su un livello (es. per Cliente) azzerando tutto il resto.
  • KEEPFILTERS: non rimuove nulla; forza un filtro aggiunto da CALCULATE a intersecarsi con il contesto esistente invece di sovrascriverlo. Senza KEEPFILTERS, un filtro su Colore="Rosso" sostituisce una eventuale selezione dell’utente sullo stesso campo.

Pattern: percentuale sul totale

% su Totale =
DIVIDE(
    [Vendite],
    CALCULATE([Vendite], REMOVEFILTERS(Prodotti[Categoria]))
)

Il numeratore usa il contesto corrente (la singola categoria di riga); il denominatore rimuove solo il filtro sulla categoria, lasciando attivi gli altri (anno, area). Scegliere la granularità giusta del REMOVEFILTERS/ALL decide se la percentuale somma al 100% per riga, per colonna o sull’intera matrice.

Pattern: confronto tra segmenti

Vendite Enterprise =
CALCULATE([Vendite], Clienti[Segmento] = "Enterprise")

Qui il filtro sovrascrive l’eventuale selezione sul segmento. Se invece si vuole rispettare uno slicer già impostato dall’utente, si avvolge il predicato in KEEPFILTERS.

La context transition

Quando CALCULATE viene invocato all’interno di un row context (una colonna calcolata, o l’iterazione di SUMX, FILTER, AVERAGEX), avviene la context transition: i valori della riga corrente vengono trasformati in filtri equivalenti sul filter context. In pratica CALCULATE “fotografa” la riga e la applica come filtro su tutte le colonne della tabella.

È l’effetto che rende funzionanti misure richiamate dentro un iteratore e le colonne calcolate che aggregano dati correlati. È anche la ragione per cui una misura, che è implicitamente avvolta in CALCULATE, si comporta diversamente da un’espressione nuda quando viene usata in SUMX. Sulle tabelle senza chiave univoca, la context transition può filtrare più righe di quelle attese: un dettaglio che l’esame ama testare.

Trappole tipiche d’esame

  • Scenario: la percentuale sul totale mostra numeri che non tornano perché il denominatore cambia al variare dei filtri di anno/area → risposta giusta: usare ALL/REMOVEFILTERS solo sulla colonna di raggruppamento (es. Prodotti[Categoria]), non sull’intera tabella, altrimenti si azzerano anche i filtri che dovrebbero restare.
  • Scenario: serve una percentuale che ignori ogni slicer e sommi sempre al 100% del grand total → risposta giusta: ALL(<tabella intera>) o ALL() senza argomenti, non ALL(<singola colonna>).
  • Scenario: aggiungendo CALCULATE(..., Colore="Rosso") la selezione dello slicer sul colore viene ignorata invece di essere ristretta → risposta giusta: avvolgere il predicato in KEEPFILTERS per intersecarlo con il contesto esistente.
  • Scenario: una misura richiamata dentro SUMX su una tabella di dettaglio dà risultati gonfiati → risposta giusta: riconoscere la context transition e verificare che la riga iterata identifichi univocamente le righe filtrate.
  • Scenario: si vuole il totale per cliente indipendente dai filtri su prodotto e data, mantenendo però il cliente → risposta giusta: ALLEXCEPT(Vendite, Clienti[Cliente]), non una combinazione di più ALL di singole colonne.