Ottimizzare i calcoli DAX
Quando l'analizzatore delle prestazioni individua una query DAX lenta, il passaggio successivo consiste nell'ottimizzare la query DAX stessa. DAX inefficiente è una delle cause più comuni delle prestazioni del modello semantico scarse. Le piccole modifiche apportate alle definizioni delle misure possono ridurre significativamente i tempi di query.
Usare le variabili per eliminare i calcoli ripetuti
Una delle tecniche di ottimizzazione DAX più efficaci consiste nell'usare le variabili (VAR / RETURN). Quando una formula valuta più volte la stessa espressione, il motore lo ricalcola ogni volta. Le variabili archiviano il risultato di un'espressione una sola volta e la riutilizzano, che può ridurre il tempo di query approssimativamente in metà per le formule con sottoespressioni ripetute.
Prendere in considerazione questa misura di crescita annuale:
Sales YoY Growth % =
DIVIDE(
([Sales] - CALCULATE([Sales], PARALLELPERIOD('Date'[Date], -12, MONTH))),
CALCULATE([Sales], PARALLELPERIOD('Date'[Date], -12, MONTH))
)
L'espressione PARALLELPERIOD valuta due volte: una volta nel numeratore e una volta nel denominatore. La riscrittura con una variabile elimina la ridondanza:
Sales YoY Growth % =
VAR SalesPriorYear =
CALCULATE([Sales], PARALLELPERIOD('Date'[Date], -12, MONTH))
RETURN
DIVIDE(([Sales] - SalesPriorYear), SalesPriorYear)
Il risultato è lo stesso, ma il motore valuta SalesPriorYear una sola volta. Le variabili migliorano anche la leggibilità: i nomi di variabili descrittivi semplificano la comprensione e la gestione delle formule complesse.
Tip
Le variabili semplificano anche il debug. È possibile modificare temporaneamente l'espressione RETURN in modo da restituire solo il valore della variabile, che consente di esaminare i risultati intermedi senza riscrivere l'intera formula.
Informazioni su FILTER e KEEPFILTERS
La modalità di applicazione delle modifiche ai filtri in DAX influisce direttamente sulle prestazioni. La FILTER funzione esegue l'iterazione su una riga di tabella per riga per valutare una condizione. Se applicato a una tabella di grandi dimensioni, questa iterazione può essere costosa.
-- Expensive: FILTER iterates the entire Sales table
High Value Sales =
CALCULATE(
[Total Sales],
FILTER(Sales, Sales[Amount] > 1000)
)
Quando si usa FILTER in una tabella con milioni di righe, il motore crea un contesto di iterazione riga per riga. Se la logica di filtro comporta solo una singola colonna, è spesso possibile sostituire FILTER con un'espressione booleana più semplice:
-- More efficient: column filter
High Value Sales =
CALCULATE(
[Total Sales],
Sales[Amount] > 1000
)
KEEPFILTERS serve uno scopo diverso. Mantiene il contesto di filtro esistente anziché sostituirlo. Usare KEEPFILTERS quando si vuole aggiungere una condizione di filtro senza eseguire l'override di ciò che è già applicato da selettori o altri oggetti visivi:
Online Sales =
CALCULATE(
[Total Sales],
KEEPFILTERS(Sales[Channel] = "Online")
)
La differenza di prestazioni tra FILTER e i predicati di colonna diretti è più evidente nelle tabelle di grandi dimensioni. Come regola generale, evitare FILTER in intere tabelle quando un predicato a livello di colonna ottiene lo stesso risultato.
Gestire i costi delle funzioni di iteratore
L'iteratore funziona come SUMX, AVERAGEXMAXX, e COUNTX valuta un'espressione per ogni riga di una tabella e quindi aggrega i risultati. Sono potenti e talvolta necessari, ma hanno un costo: soprattutto su tabelle di grandi dimensioni.
-- Iterates every row in the Sales table
Weighted Average Price =
SUMX(
Sales,
Sales[Quantity] * Sales[UnitPrice]
) / SUM(Sales[Quantity])
Questa formula funziona correttamente, ma se la tabella Sales contiene 50 milioni di righe, SUMX valuta la moltiplicazione per ogni riga. Quando esiste un'espressione non iteratore equivalente, usarla:
-- Non-iterator equivalent (if LineTotal is a column)
Total Revenue = SUM(Sales[LineTotal])
Le funzioni dell'iteratore non sono intrinsecamente cattive. Sono la scelta giusta quando è necessaria una logica di calcolo a livello di riga che non può essere espressa con un'aggregazione semplice. La chiave è comprendere il costo: le prestazioni dell'iteratore scalano con le dimensioni della tabella, quindi un iteratore su 1.000 righe è adeguato, ma lo stesso iteratore su 100 milioni di righe può diventare un collo di bottiglia.
Evitare modelli costosi
Alcuni modelli DAX sono noti per causare problemi di prestazioni. Il riconoscimento consente di scrivere formule migliori fin dall'inizio.
COUNTROWS(FILTER(...)) su tabelle di grandi dimensioni. Questo modello esegue l'iterazione di un'intera tabella per contare le righe corrispondenti a una condizione. Sostituirlo con CALCULATE e COUNTROWS con un argomento di filtro:
-- Expensive
Large Orders = COUNTROWS(FILTER(Sales, Sales[Amount] > 1000))
-- Better
Large Orders = CALCULATE(COUNTROWS(Sales), Sales[Amount] > 1000)
CALCULATE annidato con filtri complessi. Ogni CALCULATE annidato crea una nuova transizione del contesto di filtro. Le formule annidate in modo approfondito con più modifiche di contesto sono difficili da ottimizzare. Semplificare suddividendo misure complesse in misure di componenti più piccole che gestiscono una singola modifica del filtro.
Mescolare granularità di aggregazione. Le misure che combinano dati a livelli diversi di granularità (ad esempio, confrontando il valore di una singola riga con un totale a livello di tabella) richiedono transizioni di contesto che possono essere costose. Usare le variabili per valutare il totale una volta e riutilizzarlo:
Pct of Total =
VAR TotalSales = CALCULATE([Total Sales], REMOVEFILTERS())
RETURN
DIVIDE([Total Sales], TotalSales)
Spostare i calcoli nel livello dati
Se una misura DAX calcola lo stesso risultato in ogni aggiornamento della query e i dati sottostanti non cambiano tra gli aggiornamenti, valutare invece la materializzazione del calcolo nel livello dati.
Ad esempio, una colonna calcolata che concatena il nome e il cognome viene eseguita durante l'aggiornamento dei dati e archivia il risultato. La stessa logica di una misurazione verrebbe eseguita su ogni query. Per le trasformazioni statiche come questa, sono disponibili due opzioni:
- Colonne calcolate di Power Query: Definire la trasformazione nel linguaggio M durante il caricamento dei dati. Queste colonne comprimono in modo più efficiente rispetto alle colonne calcolate DAX perché il motore VertiPaq può ottimizzare l'archiviazione durante il processo di caricamento.
- Calcoli a livello di origine: se l'origine dati è un database SQL, aggiungere il calcolo alla vista o alla query SQL. In questo modo vengono usate le funzionalità di ottimizzazione del motore di database.
Riservare colonne calcolate DAX per scenari che richiedono funzioni specifiche di DAX, come la valutazione di misure o l'uso di funzioni di intelligenza temporale che dipendono dalle relazioni del modello semantico.
Important
Lo spostamento dei calcoli upstream non migliora solo le prestazioni delle query: riduce anche i tempi di aggiornamento dei dati, perché le colonne calcolate DAX vengono valutate dopo il completamento del caricamento di tutte le tabelle Power Query.
Prendere in considerazione l'esperienza di intelligenza artificiale
Le prestazioni DAX influiscono direttamente sulle esperienze basate sull'intelligenza artificiale. In Microsoft Fabric, gli agenti dati IQ e Copilot Chat eseguono query sul modello semantico generando query DAX in background. Una misura che richiede cinque secondi per un utente umano richiede gli stessi cinque secondi per Copilot: e le interazioni con intelligenza artificiale spesso hanno soglie di timeout più strette rispetto ai report interattivi.
L'ottimizzazione di DAX non riguarda solo i report più veloci. Si tratta di rendere i dati sufficientemente reattivi per supportare query in linguaggio naturale, agenti automatizzati e analisi in tempo reale.