
Expliquer la lignée dans DAX | Vers la science des données
Introduction
Dans DAX, la lignée est un concept important et il est essentiel de comprendre comment l’utiliser et le manipuler.
Comme je l’ai fait dans les articles précédents, j’utiliserai les requêtes DAX pour expliquer ce concept et ses effets.
Je commence par une simple requête pour obtenir le nombre de commandes pour le produit de la marque « Adventure Works » :
EVALUATE
CALCULATETABLE(
SUMMARIZECOLUMNS('Date'[Year]
,'Date'[MonthShortName]
,'Date'[MonthKey]
,'Product'[ProductCategoryName]
,"Order Count", [Online Order Count]
)
,'Product'[BrandName] = "Adventure Works"
)
ORDER BY 'Date'[MonthKey]
,'Product'[ProductCategoryName]
Voici un extrait du résultat de la requête :

Cette requête renvoie 180 lignes. Gardez cela à l’esprit, car cela sera important plus tard.
Ensuite, j’introduireai un filtre pour un mois spécifique et montrerai le rôle de la lignée.
Définir la lignée
J’ajouterai un filtre pour avril 2026 :
DEFINE
VAR YearMonthFilter = 202604
EVALUATE
CALCULATETABLE(
SUMMARIZECOLUMNS('Date'[Year]
,'Date'[MonthShortName]
,'Date'[MonthKey]
,'Product'[ProductCategoryName]
,"Order Count", [Online Order Count]
)
,'Product'[BrandName] = "Adventure Works"
,'Date'[MonthKey] = YearMonthFilter
)
ORDER BY 'Date'[MonthKey]
,'Product'[ProductCategoryName]
Dans ce cas, je définis une variable et fixe la valeur à 202604.
Ensuite, je l’ajoute comme filtre à la fonction CALCULATETABLE().
Rien de spécial jusqu’à présent.
Voici le résultat :

Dans ce cas, le lignage n’a pas d’importance, car une valeur scalaire définit le filtre.
Mais nous pouvons définir une lignée en utilisant le TRAITÉS() fonction:
DEFINE
VAR YearMonthFilter = TREATAS({ 202604 }, 'Date'[MonthKey])
EVALUATE
CALCULATETABLE(
SUMMARIZECOLUMNS('Date'[Year]
,'Date'[MonthShortName]
,'Date'[MonthKey]
,'Product'[ProductCategoryName]
,"Order Count", [Online Order Count]
)
,'Product'[BrandName] = "Adventure Works"
,YearMonthFilter
)
ORDER BY 'Date'[MonthKey]
,'Product'[ProductCategoryName]
Comme vous pouvez le voir, l’introduction de TREATAS() nous permet de passer la variable comme filtre. CALCULATETABLE() utilise le lignage défini par TREATAS() comme filtre sur la colonne ‘Date'[MonthKey].
Le résultat ne change pas, mais la requête est plus simple, car je n’ai pas besoin de transmettre la condition « la colonne est égale à la valeur du filtre ».

En fait, Power BI utilise ce formulaire en permanence lorsqu’il transmet les filtres définis dans un rapport au modèle sémantique.
Mais cela se passe différemment :
Il définit les variables, définit le lignage et ajoute tous les filtres directement à RÉSUMÉ LES COLONNES():
DEFINE
VAR YearMonthFilter = TREATAS({ 202604 }, 'Date'[MonthKey])
VAR SelectedBrand = TREATAS( { "Adventure Works" }, 'Product'[BrandName])
EVALUATE
SUMMARIZECOLUMNS('Date'[Year]
,'Date'[MonthShortName]
,'Date'[MonthKey]
,'Product'[ProductCategoryName]
,YearMonthFilter
,SelectedBrand
,"Order Count", [Online Order Count]
)
ORDER BY 'Date'[MonthKey]
,'Product'[ProductCategoryName]
Effacer la lignée
Vous pourriez rencontrer des situations dans lesquelles vous devrez effacer la lignée.
La méthode pour le faire varie selon que vous avez une ou plusieurs valeurs comme filtre.
Par exemple, regardez le code suivant, où j’utilise VALEUR() pour supprimer le lignage sur l’expression précédente :
DEFINE
VAR YearMonthFilter = TREATAS({ 202604 }, 'Date'[MonthKey])
VAR YearMonthFilter_cleared = VALUE(YearMonthFilter)
EVALUATE
CALCULATETABLE(
SUMMARIZECOLUMNS('Date'[Year]
,'Date'[MonthShortName]
,'Date'[MonthKey]
,'Product'[ProductCategoryName]
,"Order Count", [Online Order Count]
)
,'Product'[BrandName] = "Adventure Works"
,YearMonthFilter_cleared
)
ORDER BY 'Date'[MonthKey]
,'Product'[ProductCategoryName]
Voici l’erreur générée par Power BI :

Le moteur ne peut pas fonctionner avec le filtre de la conduite 71 car il n’a plus de lignée.
Cela fonctionnera sous cette forme :
DEFINE
VAR YearMonthFilter = TREATAS({ 202604 }, 'Date'[MonthKey])
VAR YearMonthFilter_cleared = VALUE(YearMonthFilter)
EVALUATE
CALCULATETABLE(
SUMMARIZECOLUMNS('Date'[Year]
,'Date'[MonthShortName]
,'Date'[MonthKey]
,'Product'[ProductCategoryName]
,"Order Count", [Online Order Count]
)
,'Product'[BrandName] = "Adventure Works"
,'Date'[MonthKey] = YearMonthFilter_cleared
)
ORDER BY 'Date'[MonthKey]
,'Product'[ProductCategoryName]
Comme vous pouvez le voir ici, la requête renvoie le même résultat que précédemment :

Notez le changement de l’argument de filtre à la ligne 91.
Mais il existe un moyen plus simple d’effacer la lignée lorsque l’on travaille avec des mesures.
Regardez la requête suivante avec la mesure [Order Count full year]qui calcule le nombre de commandes pour l’année entière :
DEFINE
MEASURE 'All Measures'[Order Count full year] =
VAR SelYear = TREATAS({ SELECTEDVALUE('Date'[Year]) }, 'Date'[Year])
RETURN
CALCULATE([Online Order Count]
,REMOVEFILTERS('Date')
,SelYear
)
EVALUATE
CALCULATETABLE(
SUMMARIZECOLUMNS('Date'[Year]
,'Date'[MonthShortName]
,'Date'[MonthKey]
,'Product'[ProductCategoryName]
,"Order Count", [Online Order Count]
,"Order Count full year", [Order Count full year]
)
,'Product'[BrandName] = "Adventure Works"
)
ORDER BY 'Date'[MonthKey]
,'Product'[ProductCategoryName]
Voici un extrait du résultat :

Maintenant, j’ajoute une valeur scalaire à la variable :
DEFINE
MEASURE 'All Measures'[Order Count full year] =
VAR SelYear = TREATAS({ SELECTEDVALUE('Date'[Year]) }, 'Date'[Year])
VAR SelYear_Plus1 = SelYear + 0
RETURN
CALCULATE([Online Order Count]
,REMOVEFILTERS('Date')
,SelYear_Plus1
)
EVALUATE
CALCULATETABLE(
SUMMARIZECOLUMNS('Date'[Year]
,'Date'[MonthShortName]
,'Date'[MonthKey]
,'Product'[ProductCategoryName]
,"Order Count", [Online Order Count]
,"Order Count full year", [Order Count full year]
)
,'Product'[BrandName] = "Adventure Works"
)
ORDER BY 'Date'[MonthKey]
,'Product'[ProductCategoryName]
Cette opération efface le lignage, et la mesure ne fonctionne plus :

Ce que je peux encore faire, c’est utiliser un filtre égal pour obtenir le résultat précédent :
DEFINE
MEASURE 'All Measures'[Order Count full year] =
VAR SelYear = TREATAS({ SELECTEDVALUE('Date'[Year]) }, 'Date'[Year])
VAR SelYear_Plus1 = SelYear + 0
RETURN
CALCULATE([Online Order Count]
,REMOVEFILTERS('Date')
,'Date'[Year] = SelYear_Plus1
)
EVALUATE
CALCULATETABLE(
SUMMARIZECOLUMNS('Date'[Year]
,'Date'[MonthShortName]
,'Date'[MonthKey]
,'Product'[ProductCategoryName]
,"Order Count", [Online Order Count]
,"Order Count full year", [Order Count full year]
)
,'Product'[BrandName] = "Adventure Works"
)
ORDER BY 'Date'[MonthKey]
,'Product'[ProductCategoryName]
Maintenant j’obtiens le même résultat qu’avant :

Et maintenant, utilisons plusieurs valeurs comme filtre.
Par exemple, deux mois :
DEFINE
VAR YearMonthFilter = TREATAS({ 202604, 202605 }, 'Date'[MonthKey])
EVALUATE
CALCULATETABLE(
SUMMARIZECOLUMNS('Date'[Year]
,'Date'[MonthShortName]
,'Date'[MonthKey]
,'Product'[ProductCategoryName]
,"Order Count", [Online Order Count]
)
,'Product'[BrandName] = "Adventure Works"
,YearMonthFilter
)
ORDER BY 'Date'[MonthKey]
,'Product'[ProductCategoryName]
Voici le résultat de cette requête :

Une façon de supprimer le lignage d’une variable avec plusieurs valeurs consiste à utiliser RÉSUMÉ LES COLONNES():
DEFINE
VAR YearMonthFilter = TREATAS({ 202604, 202605 }, 'Date'[MonthKey])
VAR YearMonthFilter_cleared = SUMMARIZECOLUMNS(YearMonthFilter)
EVALUATE
CALCULATETABLE(
SUMMARIZECOLUMNS('Date'[Year]
,'Date'[MonthShortName]
,'Date'[MonthKey]
,'Product'[ProductCategoryName]
,"Order Count", [Online Order Count]
)
,'Product'[BrandName] = "Adventure Works"
,YearMonthFilter_cleared
)
ORDER BY 'Date'[MonthKey]
,'Product'[ProductCategoryName]
Malheureusement, cette méthode supprime complètement le filtre et tous les mois sont renvoyés sur 180 lignes (le même nombre de lignes que pour la requête initiale) :

Techniquement, le lignage n’est pas effacé car la requête fonctionne toujours, mais le filtre mensuel est supprimé.
Mais lorsque vous essayez d’utiliser la variable « YearMonthFilter_cleared » avec un opérateur IN, cela ne fonctionne plus :

Dans ce contexte, j’ai essayé d’autres fonctions, comme DISTINCT() et VALEURS(). Alors que DISTINCT() n’a eu aucun effet, VALUES() en a.
Par exemple, alors que cette requête ne fonctionne pas :
DEFINE
VAR YearMonthFilter = TREATAS({ 202604, 202605 }, 'Date'[MonthKey])
VAR YearMonthFilter_cleared = VALUES(YearMonthFilter)
EVALUATE
CALCULATETABLE(
SUMMARIZECOLUMNS('Date'[Year]
,'Date'[MonthShortName]
,'Date'[MonthKey]
,'Product'[ProductCategoryName]
,"Order Count", [Online Order Count]
)
,'Product'[BrandName] = "Adventure Works"
,YearMonthFilter_cleared
)
ORDER BY 'Date'[MonthKey]
,'Product'[ProductCategoryName]
Voici le message d’erreur :

Cela fonctionne lors de l’utilisation d’un opérateur IN, qui indique que le lignage est effacé lors de l’utilisation de VALUES() :
DEFINE
VAR YearMonthFilter = TREATAS({ 202604, 202605 }, 'Date'[MonthKey])
VAR YearMonthFilter_cleared = VALUES(YearMonthFilter)
EVALUATE
CALCULATETABLE(
SUMMARIZECOLUMNS('Date'[Year]
,'Date'[MonthShortName]
,'Date'[MonthKey]
,'Product'[ProductCategoryName]
,"Order Count", [Online Order Count]
)
,'Product'[BrandName] = "Adventure Works"
,'Date'[MonthKey] IN YearMonthFilter
)
ORDER BY 'Date'[MonthKey]
,'Product'[ProductCategoryName]
Voici le résultat de la requête :

La documentation de VALUES() indique que cette fonction nécessite une référence de table ou de colonne.
Mais ce comportement montre que cela dépend de la manière dont la variable est utilisée dans la requête.
Comme la variable a un ensemble de lignées, VALUES() l’accepte comme référence de colonne.
Manipuler la lignée
Modifions ensuite la façon dont nous appliquons un filtre en manipulant le lignage.
Je souhaite créer un rapport montrant toutes les commandes en ligne par pays, ainsi que les commandes servies par les magasins dans chaque pays.
Par exemple, j’ai 68 commandes de clients en provenance d’Allemagne en avril 2026. Je souhaite voir combien de commandes ont été traitées par les magasins de ce pays, le cas échéant.
Quelque chose comme ça :

Je peux le faire en travaillant avec une variable :
DEFINE
MEASURE 'All Measures'[Orders served from Country] =
VAR SelCountry = SELECTEDVALUE('Customer'[RegionCountryName])
RETURN
CALCULATE([Online Order Count]
,REMOVEFILTERS(Customer[RegionCountryName])
,'Store'[RegionCountryName] = SelCountry
)
EVALUATE
CALCULATETABLE(
SUMMARIZECOLUMNS('Date'[Year]
,'Date'[Month]
,'Date'[MonthKey]
,'Customer'[RegionCountryName]
,"Order Count", [Online Order Count]
,"Order Count Country Check", [Order Count Country Check]
)
,'Product'[BrandName] = "Adventure Works"
)
ORDER BY 'Date'[Year]
,'Date'[Month]
,'Customer'[RegionCountryName]
Dans cette approche, je stocke le pays actuel dans une variable. Ensuite, je supprime le filtre de Client[RegionCountryName]. Et je le remplace par un filtre sur ‘Store'[RegionCountryName].
Voici le résultat :

Ou je peux le faire de cette façon en utilisant TREATAS() :
DEFINE
MEASURE 'All Measures'[Orders served from Country] =
CALCULATE([Online Order Count]
,REMOVEFILTERS(Customer[RegionCountryName])
,TREATAS(VALUES('Customer'[RegionCountryName])
,'Store'[RegionCountryName])
)
EVALUATE
CALCULATETABLE(
SUMMARIZECOLUMNS('Date'[Year]
,'Date'[Month]
,'Date'[MonthKey]
,'Customer'[RegionCountryName]
,"Order Count", [Online Order Count]
,"Orders served from Country", [Orders served from Country]
)
,'Product'[BrandName] = "Adventure Works"
)
ORDER BY 'Date'[Year]
,'Date'[Month]
,'Customer'[RegionCountryName]
Dans cette approche, je supprime à nouveau le filtre de Client[RegionCountryName]. Mais ensuite j’utilise TREATAS() pour changer la lignée de filtre du pays actuel sur « Store »[RegionCountryName].
Dans cette approche, je n’ai pas besoin de variable ; Je peux filtrer directement le tableau des magasins par pays actuel.
Ce code est beaucoup plus court mais peut être plus difficile à comprendre pour les lecteurs qui ne savent pas comment fonctionne TREATAS().
Travailler avec des tableaux
Puisque nous pouvons créer des tables ad hoc dans DAX, nous pourrions rencontrer des problèmes car ces tables manquent de lignage.
Je peux commencer à écrire du code à ce sujet, mais SQLBI l’a déjà fait, et vous pouvez lire leur article, qui est très bien expliqué :
Vous pouvez le trouver ici :
https://www.sqlbi.com/articles/understanding-data-lineage-in-dax
Conclusion
Le concept de lignée peut être difficile à comprendre, même si nous l’utilisons tout le temps lorsque nous travaillons avec des filtres dans DAX.
Power BI génère du code à l’aide de TREATAS() à tout moment lorsqu’il applique des filtres de rapport.
Et parfois, cela peut conduire à un code DAX plus simple lorsque vous savez comment le manipuler efficacement.
Cela peut devenir vital lorsque je crée une table avec DAX à partir de tables existantes. La table conservera la lignée. Cela peut entraîner des problèmes lorsque j’essaie d’ajouter des relations aux tables sources. Sans effacer la lignée, je rencontrerai une erreur due à une dépendance circulaire.
Je vous encourage à commencer à expérimenter les concepts présentés ici et à essayer d’optimiser votre code DAX.
Bien que « optimiser » ne soit pas le bon mot, car je n’ai remarqué aucune amélioration des performances lors de l’utilisation des variantes présentées.
Mais avoir un code DAX plus court et plus facile à lire peut être une optimisation en soi.
Amusez-vous à travailler avec.
Références
Un article SQLBI sur l’utilisation du lignage :
https://www.sqlbi.com/articles/understanding-data-lineage-in-dax
Comme dans mes articles précédents, j’utilise l’exemple de jeu de données Contoso. Vous pouvez télécharger gratuitement l’ensemble de données ContosoRetailDW depuis Microsoft. ici.
Les données Contoso peuvent être utilisées librement sous la licence MIT, comme décrit dans ce document. J’ai modifié l’ensemble de données pour déplacer les données vers des dates contemporaines.



