Tutti gli articoli
BlogDAXMisure

CALCULATE in DAX: la funzione più importante, spiegata bene

28 luglio 2026 8 min di letturadi Valerio Barbagallo

CALCULATE è l'unica funzione DAX che può modificare il contesto di filtro di un'espressione. Fa due cose: applica i filtri che le passi (sovrascrivendo quelli esistenti sulle stesse colonne) e, se viene invocata dentro un contesto di riga, lo trasforma in contesto di filtro — la famosa context transition. Tutto il resto di DAX si costruisce sopra questi due comportamenti.

La sintassi

CALCULATE (
    <espressione>,
    <filtro1>,
    <filtro2>,
    ...
)

L'espressione deve restituire uno scalare (tipicamente un'aggregazione o un'altra misura). I filtri possono essere di due tipi:

  • Predicato booleano: Vendite[Area] = "Nord". Deve riferirsi a una sola colonna.
  • Espressione tabella: FILTER(...), ALL(...), VALUES(...), TREATAS(...).

Ordine di valutazione — è il punto che spiega metà dei comportamenti "strani": i filtri vengono valutati nel contesto esterno, cioè quello che esisteva prima di CALCULATE. Solo dopo vengono applicati, e l'espressione viene valutata nel nuovo contesto.

Esempio 1 — filtro semplice, e cosa significa "sovrascrivere"

Fatturato Nord =
CALCULATE (
    SUM ( Vendite[Importo] ),
    Vendite[Area] = "Nord"
)

Sotto il cofano il predicato booleano viene tradotto dal motore in:

FILTER ( ALL ( Vendite[Area] ), Vendite[Area] = "Nord" )

Nota quell'ALL. È il motivo per cui CALCULATE sostituisce il filtro esistente su Vendite[Area] invece di aggiungersi ad esso. In una matrice con le aree sulle righe, Fatturato Nord mostra il valore del Nord su tutte le righe, anche su quella "Sud". Non è un bug: è la semantica di CALCULATE.

Filtri multipli sono in AND fra loro, ma ciascuno sovrascrive solo la propria colonna:

Fatturato Nord Online =
CALCULATE (
    SUM ( Vendite[Importo] ),
    Vendite[Area]   = "Nord",
    Vendite[Canale] = "Online"
)

Esempio 2 — percentuale sul totale con ALL / REMOVEFILTERS

Per avere un denominatore "senza filtri", rimuovi il filtro dalla colonna che sta sulle righe del visual:

% sul Totale Categoria =
DIVIDE (
    SUM ( Vendite[Importo] ),
    CALCULATE (
        SUM ( Vendite[Importo] ),
        REMOVEFILTERS ( Prodotti[Categoria] )
    )
)

REMOVEFILTERS è l'alias moderno di ALL quando usato come modificatore di filtro dentro CALCULATE. Fanno la stessa cosa; REMOVEFILTERS dichiara l'intento ed è più leggibile. ALL resta necessario quando ti serve una tabella (es. dentro SUMX o COUNTROWS), perché REMOVEFILTERS non può essere usato come funzione tabella.

Le varianti che userai davvero:

Funzione Cosa rimuove Uso tipico
REMOVEFILTERS(Tab[Col]) Il filtro su quella colonna % sul totale di una dimensione
REMOVEFILTERS(Tab) Tutti i filtri sulla tabella (e sulla sua tabella espansa) Totale generale
ALLEXCEPT(Tab, Col1, …) Tutto tranne le colonne elencate % sul totale del proprio gruppo
ALLSELECTED(Tab[Col]) I filtri interni al visual, non quelli esterni (slicer, filtri pagina) % sul totale visualizzato
ALLNOBLANKROW(Tab) Come ALL, ma esclude la riga vuota da relazioni non valide Modelli con integrità referenziale imperfetta

Percentuale sul totale del proprio paese, non sul globale:

% sul Totale Paese =
DIVIDE (
    SUM ( Vendite[Importo] ),
    CALCULATE (
        SUM ( Vendite[Importo] ),
        ALLEXCEPT ( Clienti, Clienti[Paese] )
    )
)

Esempio 3 — KEEPFILTERS, quando vuoi intersecare invece che sostituire

Torniamo al problema della matrice per area. Se vuoi che Fatturato Nord sia valorizzato solo sulla riga Nord e vuoto sulle altre, avvolgi il predicato in KEEPFILTERS:

Fatturato Nord (KEEPFILTERS) =
CALCULATE (
    SUM ( Vendite[Importo] ),
    KEEPFILTERS ( Vendite[Area] = "Nord" )
)

KEEPFILTERS cambia la semantica da sostituzione a intersezione: il nuovo filtro si combina in AND con quello già presente. Sulla riga "Sud" l'intersezione fra {Sud} e {Nord} è vuota → BLANK.

Vale anche per filtri su liste di valori. Questo è il pattern corretto per "fatturato dei prodotti A e B, ma rispettando la selezione dello slicer":

Fatturato A e B =
CALCULATE (
    SUM ( Vendite[Importo] ),
    KEEPFILTERS ( Prodotti[Categoria] IN { "Accessori", "Bevande" } )
)

La context transition (il pezzo che confonde tutti)

Secondo comportamento di CALCULATE, indipendente dai filtri: se esiste un contesto di riga, CALCULATE lo trasforma in contesto di filtro.

Il contesto di riga esiste in due situazioni: dentro una colonna calcolata e dentro un iteratore (SUMX, AVERAGEX, FILTER, ADDCOLUMNS).

Esempio senza CALCULATE:

-- SBAGLIATO: restituisce il totale generale su ogni riga
Media per Cliente =
AVERAGEX ( Clienti, SUM ( Vendite[Importo] ) )

AVERAGEX itera i clienti, ma il contesto di riga su Clienti non filtra Vendite. Le relazioni propagano il contesto di filtro, non quello di riga. Risultato: ogni iterazione somma tutto, la media è il totale generale.

Con CALCULATE:

Media per Cliente =
AVERAGEX (
    Clienti,
    CALCULATE ( SUM ( Vendite[Importo] ) )
)

Qui CALCULATE non riceve nessun filtro, e sembrerebbe inutile. Invece è tutto: converte "la riga corrente di Clienti" in "un filtro su quel cliente", il filtro si propaga a Vendite lungo la relazione, e la somma diventa quella del singolo cliente.

Chiamare una misura equivale a chiamarla dentro CALCULATE. Ogni riferimento a una misura ha un CALCULATE implicito attorno. Quindi questo funziona identico al codice sopra:

Media per Cliente = AVERAGEX ( Clienti, [Fatturato Totale] )

È la ragione per cui una misura scritta dentro un iteratore "magicamente" funziona, mentre la stessa espressione scritta per esteso no. Sapere che il CALCULATE implicito esiste evita ore di confusione.

CALCULATE in una colonna calcolata vs in una misura

In una colonna calcolata il contesto di riga c'è sempre, quindi CALCULATE fa context transition:

-- Colonna calcolata nella tabella Clienti
Fatturato Cliente =
CALCULATE ( SUM ( Vendite[Importo] ) )

Senza CALCULATE la stessa formula darebbe il totale generale su ogni riga.

Tre avvertenze oneste:

  1. Una colonna calcolata è materializzata al refresh e occupa memoria. Su una dimensione da 50k clienti è irrilevante; su una fact da 100 milioni di righe è una scelta architetturale, non una comodità.
  2. La context transition usa tutte le colonne della riga corrente come filtro. Su una tabella con righe duplicate (tipico delle fact senza chiave), righe identiche vengono raggruppate: il risultato non è "questa riga" ma "tutte le righe uguali a questa".
  3. Se il valore ti serve solo per un visual, spesso una misura è meglio: si adatta al contesto e non pesa sul modello.

In una misura valutata in un visual non c'è contesto di riga, quindi CALCULATE senza filtri non fa nulla di utile: CALCULATE(SUM(Vendite[Importo])) è equivalente a SUM(Vendite[Importo]).

Errori comuni

Filtrare su una misura. Questo non compila:

-- ERRORE
Clienti Profittevoli =
CALCULATE (
    COUNTROWS ( Clienti ),
    [Margine] > 0
)

Il messaggio è "A function 'PLACEHOLDER' has been used in a True/False expression that is used as a table filter expression". Un predicato booleano in CALCULATE deve riferirsi a colonne, non a misure. La forma corretta usa FILTER su una tabella, dove la misura viene valutata riga per riga (con context transition):

Clienti Profittevoli =
CALCULATE (
    COUNTROWS ( Clienti ),
    FILTER ( VALUES ( Clienti[IdCliente] ), [Margine] > 0 )
)

Nota VALUES(Clienti[IdCliente]) e non Clienti: itera solo la colonna che ti serve, non l'intera tabella. Su dimensioni larghe la differenza di performance è misurabile.

Predicato booleano su più colonne. Anche questo dà errore:

-- ERRORE: due colonne nello stesso predicato
CALCULATE ( SUM ( Vendite[Importo] ), Vendite[Importo] > Vendite[Costo] )

Serve FILTER:

CALCULATE (
    SUM ( Vendite[Importo] ),
    FILTER ( Vendite, Vendite[Importo] > Vendite[Costo] )
)

Usare FILTER quando basta un predicato. L'errore opposto, e più diffuso:

-- Funziona, ma è lento
CALCULATE ( SUM ( Vendite[Importo] ), FILTER ( Vendite, Vendite[Area] = "Nord" ) )

-- Meglio: il motore lo risolve sulla singola colonna
CALCULATE ( SUM ( Vendite[Importo] ), Vendite[Area] = "Nord" )

FILTER(Vendite, …) materializza un'iterazione sull'intera fact table. Il predicato booleano lavora sulla sola colonna Area, che ha pochi valori distinti. Su tabelle grandi si parla di ordini di grandezza. FILTER va usato quando la condizione coinvolge più colonne o una misura — non come abitudine.

Confondere ALL e ALLSELECTED. ALL ignora tutto, slicer compresi: se l'utente seleziona 3 categorie su 20, il denominatore resta il totale delle 20 e le percentuali non sommano a 100%. ALLSELECTED rispetta i filtri esterni al visual e restituisce il totale delle 3 selezionate. Regola pratica: se la percentuale deve sommare a 100% nel visual, ti serve ALLSELECTED.

Sottovalutare REMOVEFILTERS sulla fact table. REMOVEFILTERS(Vendite) non rimuove solo i filtri sulle colonne di Vendite: agisce sulla tabella espansa, che include le dimensioni collegate lato uno (Prodotti, Clienti, Calendario). Spesso è ciò che vuoi (totale generale), ma se cercavi "rimuovi solo il filtro sul canale" hai appena azzerato anche l'anno e la categoria. Sii specifico: REMOVEFILTERS(Vendite[Canale]).

Filtrare la data sulla fact invece che sulla calendario. CALCULATE(SUM(Vendite[Importo]), YEAR(Vendite[Data]) = 2026) produce un numero corretto ma scollegato dalla dimensione tempo: non interagisce con gli slicer sul calendario e non si combina con le funzioni di time intelligence. Filtra sempre Calendario[Anno] = 2026.

Annidare CALCULATE senza pensare all'ordine. In CALCULATE(CALCULATE(expr, filtroA), filtroB), filtroB viene applicato per primo e filtroA per ultimo — quello interno vince sulle colonne in comune. Quando le misure si compongono su tre livelli, questa è la fonte tipica di risultati inspiegabili. Se il codice diventa difficile da leggere, spezzalo in variabili con VAR.

Un riassunto in tre righe

  • CALCULATE sostituisce i filtri sulle colonne che tocca; KEEPFILTERS li interseca.
  • I filtri di CALCULATE si valutano nel contesto esterno; l'espressione nel contesto nuovo.
  • Dentro un contesto di riga, CALCULATE fa context transition — ed è ciò che accade ogni volta che chiami una misura dentro un iteratore.

Se vuoi arrivare a CALCULATE con le basi già solide — modello, relazioni, contesto di filtro e di riga spiegati prima delle formule — il Livello Base del corso Power BI Lab è gratuito e copre modellazione e prime misure; DAX avanzato e time intelligence stanno nei livelli successivi (prezzi).

Continua a leggere