Optimiser les calculs DAX

Effectué

Lorsque l’analyseur de performances identifie une requête DAX lente, l’étape suivante consiste à optimiser le DAX lui-même. DAX inefficace est l’une des causes les plus courantes des performances médiocres du modèle sémantique. Les petites modifications apportées aux définitions de mesure peuvent réduire considérablement les temps de requête.

Utiliser des variables pour éliminer les calculs répétés

L’une des techniques d’optimisation DAX les plus efficaces consiste à utiliser des variables (VAR / RETURN). Lorsqu’une formule évalue la même expression plusieurs fois, le moteur le recalcule à chaque fois. Les variables stockent le résultat d’une expression une fois et la réutilisez, ce qui peut réduire le temps de requête environ en moitié pour les formules avec des sous-expressions répétées.

Tenez compte de cette mesure de croissance annuelle :

Sales YoY Growth % =
DIVIDE(
    ([Sales] - CALCULATE([Sales], PARALLELPERIOD('Date'[Date], -12, MONTH))),
    CALCULATE([Sales], PARALLELPERIOD('Date'[Date], -12, MONTH))
)

L’expression PARALLELPERIOD s’évalue deux fois : une fois dans le numérateur et une fois dans le dénominateur. La réécriture avec une variable élimine la redondance :

Sales YoY Growth % =
VAR SalesPriorYear =
    CALCULATE([Sales], PARALLELPERIOD('Date'[Date], -12, MONTH))
RETURN
    DIVIDE(([Sales] - SalesPriorYear), SalesPriorYear)

Le résultat est le même, mais le moteur ne SalesPriorYear l’évalue qu’une seule fois. Les variables améliorent également la lisibilité : les noms de variables descriptives facilitent la compréhension et la maintenance des formules complexes.

Conseil / Astuce

Les variables simplifient également le débogage. Vous pouvez modifier temporairement l’expression RETURN pour générer uniquement la valeur de la variable, ce qui vous permet d’inspecter les résultats intermédiaires sans réécrire la formule entière.

Comprendre FILTER et KEEPFILTERS

La façon dont vous appliquez des modifications de filtre dans DAX affecte directement les performances. La FILTER fonction itère sur une ligne de table par ligne pour évaluer une condition. Lorsqu’elle est appliquée à une table volumineuse, cette itération peut être coûteuse.

-- Expensive: FILTER iterates the entire Sales table
High Value Sales =
CALCULATE(
    [Total Sales],
    FILTER(Sales, Sales[Amount] > 1000)
)

Lorsque vous utilisez FILTER sur une table avec des millions de lignes, le moteur crée un contexte d'itération ligne-à-ligne. Si la logique de filtre implique uniquement une seule colonne, vous pouvez souvent remplacer FILTER par une expression booléenne plus simple :

-- More efficient: column filter
High Value Sales =
CALCULATE(
    [Total Sales],
    Sales[Amount] > 1000
)

KEEPFILTERS sert un autre but. Il conserve le contexte de filtre existant au lieu de le remplacer. Utilisez KEEPFILTERS lorsque vous souhaitez ajouter une condition de filtrage sans effectuer de remplacement sur ce qui est déjà appliqué par les segmentateurs ou d’autres visuels :

Online Sales =
CALCULATE(
    [Total Sales],
    KEEPFILTERS(Sales[Channel] = "Online")
)

La différence de performances entre FILTER et les prédicats de colonne directe est particulièrement notable sur les tables volumineuses. En règle générale, évitez l'application de FILTER à des tables entières lorsqu’un prédicat au niveau des colonnes obtient le même résultat.

Gérer les coûts de fonction d’itérateur

L’itérateur fonctionne comme SUMX, AVERAGEX, MAXXet COUNTX évalue une expression pour chaque ligne d’une table, puis agrège les résultats. Ils sont puissants et parfois nécessaires, mais ils ont un coût, ce coût est particulièrement ressenti sur les grandes tables.

-- Iterates every row in the Sales table
Weighted Average Price =
SUMX(
    Sales,
    Sales[Quantity] * Sales[UnitPrice]
) / SUM(Sales[Quantity])

Cette formule fonctionne correctement, mais si la table Sales a 50 millions de lignes, SUMX évalue la multiplication pour chaque ligne. Lorsqu’une expression non itérateur équivalente existe, utilisez-la :

-- Non-iterator equivalent (if LineTotal is a column)
Total Revenue = SUM(Sales[LineTotal])

Les fonctions d’itérateur ne sont pas intrinsèquement mauvaises. Ils sont le bon choix lorsque vous avez besoin d’une logique de calcul au niveau des lignes qui ne peut pas être exprimée avec une agrégation simple. La clé est de comprendre le coût : les performances de l'itérateur sont proportionnelles à la taille de la table, de sorte qu'un itérateur sur 1 000 lignes est satisfaisant, mais le même itérateur sur 100 millions de lignes peut devenir un goulot d'étranglement.

Éviter les modèles coûteux

Certains modèles DAX sont connus pour provoquer des problèmes de performances. La reconnaissance de ces formules vous aide à écrire de meilleures formules dès le début.

COUNTROWS(FILTER(...)) sur les tables volumineuses. Ce modèle itère une table entière pour compter les lignes correspondant à une condition. Remplacez-le par CALCULATE et COUNTROWS avec un argument de filtre :

-- Expensive
Large Orders = COUNTROWS(FILTER(Sales, Sales[Amount] > 1000))

-- Better
Large Orders = CALCULATE(COUNTROWS(Sales), Sales[Amount] > 1000)

Calculate imbriqué avec des filtres complexes. Chaque élément imbriqué CALCULATE crée une transition de contexte de filtre. Les formules profondément imbriquées avec plusieurs modifications de contexte sont difficiles à optimiser. Simplifiez en cassant des mesures complexes en mesures de composants plus petites qui gèrent chacune une modification de filtre unique.

Mélange de grains d’agrégation. Les mesures qui combinent des données à différents niveaux de granularité (par exemple, la comparaison de la valeur d’une ligne unique à un total de niveau table) nécessitent des transitions de contexte qui peuvent être coûteuses. Utilisez des variables pour évaluer le total une fois et le réutiliser :

Pct of Total =
VAR TotalSales = CALCULATE([Total Sales], REMOVEFILTERS())
RETURN
    DIVIDE([Total Sales], TotalSales)

Déplacer des calculs vers la couche de données

Si une mesure DAX calcule le même résultat sur chaque actualisation de requête et que les données sous-jacentes ne changent pas entre les actualisations, envisagez de matérialiser ce calcul dans la couche de données à la place.

Par exemple, une colonne calculée qui concatène le prénom et le nom s’exécute pendant l’actualisation des données et stocke le résultat. Mais la même logique qu’une mesure s’appliquerait à chaque requête. Pour les transformations statiques telles que celles-ci, vous avez deux options :

  • Colonnes calculées de Power Query : Définissez la transformation en M pendant le chargement des données. Ces colonnes compressent plus efficacement que les colonnes calculées DAX, car le moteur VertiPaq peut optimiser le stockage pendant la charge.
  • Calculs au niveau source : si la source de données est une base de données SQL, ajoutez le calcul à la vue ou à la requête SQL. Cela utilise les fonctionnalités d’optimisation du moteur de base de données.

Réservez les colonnes calculées DAX aux scénarios qui nécessitent des fonctions spécifiques à DAX, telles que l’évaluation de mesures ou l’utilisation de fonctions d’intelligence temporelle qui dépendent des relations du modèle sémantique.

Important

Le déplacement de calculs en amont n'améliore pas seulement les performances des requêtes : cela réduit également les temps d'actualisation des données, car les colonnes calculées DAX sont évaluées une fois que toutes les tables Power Query finissent de charger.

Considérez l’expérience IA

Les performances DAX affectent directement les expériences basées sur l’IA. Dans Microsoft Fabric, les agents de données IQ et Copilot conversation interrogent votre modèle sémantique en générant des requêtes DAX en arrière-plan. Une mesure qui prend cinq secondes pour un utilisateur humain prend les mêmes cinq secondes pour Copilot, tandis que les interactions IA ont souvent des seuils de délai d'expiration plus stricts que les rapports interactifs.

L’optimisation de DAX ne concerne pas seulement les rapports plus rapides. Il s’agit de rendre vos données suffisamment réactives pour prendre en charge les requêtes en langage naturel, les agents automatisés et l’analytique en temps réel.