🎯 Situation
Il y a quelques semaines, je passais en revue un rapport Power BI qui prenait entre 8 et 12 secondes à charger chaque visuel. Le modèle de données était correct — pas parfait, mais ce n'était pas le problème. Le problème, c'était le DAX.
Des IF imbriqués dans un SUMX qui itérait sur 2 millions de lignes. Des colonnes calculées faisant ce que des mesures devraient faire. Des FILTER matérialisant des tables dans CALCULATE. Tout corrigeable. Des patterns que je retrouve dans presque tous les projets que je récupère.
Voici les 10 erreurs les plus fréquentes — et exactement comment les corriger.
⚠️ Challenge
La plupart des personnes qui apprennent Power BI viennent d'Excel. C'est un bon point de départ — et un modèle mental dangereux pour DAX.
📄 Formules Excel
- Évaluées cellule par cellule
- De gauche à droite, de haut en bas
- Immédiates, familières, prévisibles
- Les IF imbriqués sont monnaie courante
- Les performances sont rarement un enjeu à petite échelle
⚡ Mesures DAX
- Évaluées dans un contexte de filtre
- Exécutées par deux moteurs distincts (storage + formula)
- Les performances dépendent de la capacité du moteur à optimiser
- L'itération est coûteuse — à éviter autant que possible
- La structure et le nommage comptent autant que la logique
Il ne s'agit pas d'éviter DAX — il s'agit de comprendre comment il fonctionne réellement. Ces 10 patterns couvrent 80% du chemin.
🔍 Les 10 erreurs
Chaque erreur ci-dessous inclut un avant, un après, et la raison pour laquelle ça compte. Certaines améliorent la lisibilité. D'autres apportent des gains de performance majeurs. Toutes méritent d'être corrigées.
#1 — IF imbriqués à l'infini → utiliser SWITCH
IF([Score] = "A", 1,
IF([Score] = "B", 2,
IF([Score] = "C", 3, 0)
)
)
SWITCH([Score],
"A", 1,
"B", 2,
"C", 3,
0 -- default
)
- Plus lisible et facile à étendre
- Plus rapide : SWITCH court-circuite dès la correspondance trouvée
- Ajouter un cas ne requiert aucune imbrication supplémentaire
#2 — ISBLANK partout → utiliser COALESCE
IF(ISBLANK([Revenue]), 0, [Revenue])
COALESCE([Revenue], 0)
- Deux fois moins de caractères, même résultat
- Gère à la fois BLANK et NULL
- Standard DAX moderne — à utiliser systématiquement
#3 — Répéter des calculs → utiliser VAR
IF(
[Revenue] - [Cost] > 0,
([Revenue] - [Cost]) / [Revenue],
0
)
VAR Profit = [Revenue] - [Cost]
RETURN
IF(
Profit > 0,
DIVIDE(Profit, [Revenue]),
0
)
- Chaque expression est évaluée une seule fois
- Lisible comme du vrai code, pas une formule
- Debug facile : évaluer VAR par VAR
#4 — Division classique → utiliser DIVIDE
[Profit] / [Revenue]
DIVIDE([Profit], [Revenue])
- Gère automatiquement les divisions par zéro (retourne BLANK par défaut)
- Évite les erreurs silencieuses dans les visuels
- Accepte un troisième argument optionnel comme valeur de repli
#5 — FILTER inutile dans CALCULATE
CALCULATE(
SUM(Sales[Amount]),
FILTER(Sales, Sales[Region] = "Canada")
)
CALCULATE(
SUM(Sales[Amount]),
Sales[Region] = "Canada"
)
- FILTER matérialise une table entière — coûteux à grande échelle
- Le prédicat direct est optimisé par le moteur VertiPaq
- L'un des gains de performance les plus impactants en DAX
#6 — COUNTROWS(FILTER(…)) → utiliser CALCULATE
COUNTROWS(
FILTER(Sales, Sales[Status] = "Won")
)
CALCULATE(
COUNTROWS(Sales),
Sales[Status] = "Won"
)
- FILTER itère chaque ligne avant de compter
- CALCULATE pousse le filtre au moteur de stockage
- Même résultat — nettement plus rapide sur les grandes tables
#7 — SUMX inutile → utiliser SUM
SUMX(Sales, Sales[Amount])
SUM(Sales[Amount])
- SUMX itère ligne par ligne (formula engine)
- SUM tourne dans le storage engine — beaucoup plus rapide
- Réserver SUMX aux expressions ligne par ligne : SUMX(Sales, Sales[Qty] * Sales[Price])
#8 — Colonnes calculées pour des agrégations dynamiques
-- Calculated column (wrong approach)
Margin % = Sales[Profit] / Sales[Revenue]
-- DAX measure (correct approach)
Margin % = DIVIDE(SUM(Sales[Profit]), SUM(Sales[Revenue]))
- Les colonnes sont calculées au refresh et stockées en mémoire
- Les mesures sont calculées à la requête — toujours sensibles au contexte
- Une mesure s'adapte automatiquement à chaque slicer, filtre et visuel
#9 — ALL sur la table de faits — trop large
CALCULATE([Revenue], ALL(Sales))
CALCULATE([Revenue], ALL(DimDate))
- ALL(Sales) retire tous les filtres sur la table de faits — rarement ce qu'on veut
- Cibler une dimension spécifique est précis et prévisible
- Cas d'usage fréquent : % du total en ignorant uniquement le filtre de date
#10 — Une mesure de 30 lignes sans structure
-- Everything inline, nothing named
[Revenue] - [Cost] / ([Revenue] - [Cost] + [Overhead])
VAR TotalRevenue = SUM(Sales[Revenue])
VAR TotalCost = SUM(Sales[Cost])
VAR Overhead = [OverheadMeasure]
VAR NetProfit = TotalRevenue - TotalCost - Overhead
RETURN
DIVIDE(NetProfit, TotalRevenue)
- Debuggable : commenter RETURN et le remplacer par un nom de VAR
- Auto-documenté : les noms de variables expliquent la logique
- Réutilisable : extraire n'importe quelle VAR en mesure de base
✓️ Les 3 vrais leviers
Corriger les erreurs DAX une par une, c'est utile. Mais pour comprendre pourquoi certains rapports sont rapides et d'autres lents, il n'y a que trois choses qui comptent :
- La modélisation en premier (80% du problème) — un star schema, des relations propres et des tables de dimensions nettoyées résolvent la plupart des problèmes de performance avant d'écrire une seule ligne de DAX
- Le DAX optimisé ensuite — éviter l'itération (SUMX, FILTER) quand l'agrégation (SUM, CALCULATE) fait le travail ; utiliser VAR pour éliminer les évaluations répétées
- Réduire la cardinalité et l'itération en dernier — les colonnes à haute cardinalité dans les tables de faits, les imbrications CALCULATE inutiles, et les DIVIDE manquants constituent les goulots d'étranglement restants
💡 Synthèse
Pas besoin de tout mémoriser d'un coup. Commence par les patterns qui s'appliquent à ton rapport actuel et corrige-les un par un. Les gains se cumulent.
Les trois corrections au meilleur rapport effort/résultat :
- Remplacer FILTER par des prédicats directs dans CALCULATE — gain de performance immédiat sur toute table volumineuse
- Passer à VAR + RETURN dans toute mesure avec des expressions répétées — lisibilité et performance en un seul mouvement
- Remplacer SUMX par SUM quand il n'y a pas d'expression ligne par ligne — t'oblige à réfléchir à quand l'itération est vraiment nécessaire
DAX récompense la structure. Plus tu es intentionnel dans ce que tu écris — et pourquoi — plus tes rapports sont rapides et faciles à maintenir.
👉 Un rapport Power BI lent n'est presque jamais un problème Power BI.
C'est un problème DAX. Et les problèmes DAX ont des patterns.