Plusieurs tables de faits
En bref
- Objectifs : construire un modèle en constellation ; relier des faits de même granularité par des dimensions conformes ; gérer des faits plus agrégés (objectifs mensuels) ; utiliser TREATAS pour une relation virtuelle.
- Prérequis : pages Schéma en étoile et Relations ; notions de CALCULATE.
- Niveau : intermédiaire.
Pourquoi ?
Un modèle Power BI contient rarement une seule table de faits : ventes et objectifs, commandes et stocks, budget et réalisé. Ce modèle à plusieurs étoiles est appelé modèle en constellation (ou schéma en galaxie). Mal relier ces tables produit des résultats faux ou incompréhensibles. Source
Notions clés
Les règles de base
- Chaque table de faits a sa propre granularité, constante sur toutes ses lignes (une ligne par ligne de commande, une ligne par mois et par catégorie). Source
- Les tables de faits ne sont jamais reliées entre elles : elles communiquent par les dimensions qu'elles partagent.
- Les dimensions partagées sont appelées dimensions conformes : même clé, mêmes attributs, utilisées par toutes les étoiles. La table de dates en est l'exemple type. Source
- Les relations restent en un-à-plusieurs, à sens unique, de la dimension vers chaque table de faits.
- Les colonnes numériques des tables de faits sont masquées : on ne présente que des mesures.
Exemple fil rouge
| Table | Type | Granularité |
| DimDate | Dimension conforme | Jour |
| DimProduct | Dimension conforme | Produit (avec Catégorie) |
| DimStore | Dimension conforme | Magasin |
| FactSales | Faits | Jour × produit × magasin |
| FactInventory | Faits | Jour × produit × magasin (photo du stock) |
| FactTarget | Faits | Mois × catégorie |
[Capture : vue Modèle du fil rouge, les trois dimensions au centre reliées en un-à-plusieurs à FactSales, FactInventory et FactTarget, sans relation entre les tables de faits]
Cas 1 | Même granularité : dimensions partagées
FactSales et FactInventory ont la même granularité.
- Reliez DimDate, DimProduct et DimStore à chacune des deux tables de faits (1:*, sens unique).
- Un segment sur DimProduct[Catégorie] filtre alors les ventes et les stocks en même temps.
- Les axes et segments d'un visuel qui combine les deux tables doivent toujours provenir des dimensions, jamais d'une table de faits.
Ventes = SUM ( FactSales[Montant] )
Stock fin de période =
CALCULATE (
SUM ( FactInventory[Quantité] ),
LASTDATE ( DimDate[Date] )
)
Taux de rotation = DIVIDE ( [Ventes], [Stock fin de période] )Un stock est un fait semi-additif : on ne l'additionne pas dans le temps, d'où LASTDATE ou LASTNONBLANK. Source
Cas 2 | Granularité différente : faits plus agrégés
FactTarget est stockée au mois et à la catégorie, à un niveau plus élevé que DimDate (jour) et DimProduct (produit).
Côté date
- Stockez dans FactTarget le premier jour de la période (le 01/03/2026 pour mars 2026), en type Date.
- Créez une relation 1:* classique entre DimDate[Date] et FactTarget[Mois]. Source
Côté produit
Trois solutions, de la plus recommandée à la plus ponctuelle :
- Dimension Catégorie dédiée : créez DimCategory (une ligne par catégorie), reliée en 1:* à FactTarget et à DimProduct. C'est la solution la plus lisible.
- Relation plusieurs-à-plusieurs DimProduct[Catégorie] *:* FactTarget[Catégorie], avec un filtrage unidirectionnel de DimProduct vers FactTarget. Source
- Relation virtuelle avec TREATAS, sans relation physique (cas 3).
Contrôler l'agrégation
Sans précaution, un objectif mensuel affiché au jour ou au produit donne un résultat faux (vide ou dupliqué). La mesure doit renvoyer BLANK sous la granularité de la table :
Objectif =
IF (
NOT ISFILTERED ( DimDate[Date] )
&& NOT ISFILTERED ( DimProduct[Produit] ),
SUM ( FactTarget[Montant objectif] )
)
Écart à l'objectif = [Ventes] - [Objectif]
% Atteinte = DIVIDE ( [Ventes], [Objectif] )Une variante consiste à répartir l'objectif au jour, au prorata du nombre de jours du mois. Ce calcul plus avancé est à réserver aux besoins avérés.
Cas 3 | Relation virtuelle avec TREATAS
TREATAS applique les valeurs d'une colonne comme filtre sur une autre colonne, sans relation dans le modèle. C'est utile pour un besoin ponctuel ou pour éviter une relation *:* ambiguë. Source
Objectif (TREATAS) =
CALCULATE (
SUM ( FactTarget[Montant objectif] ),
TREATAS ( VALUES ( DimProduct[Catégorie] ), FactTarget[Catégorie] )
)- Avantage : pas de relation supplémentaire, pas de risque d'ambiguïté.
- Inconvénient : la logique doit être reprise dans chaque mesure, et une relation virtuelle est généralement moins efficace qu'une relation physique sur de gros volumes. Source
Pièges et bonnes pratiques
- Ne reliez jamais deux tables de faits directement (relation *:* entre FactSales et FactTarget) : passez par des dimensions communes. Source
- Ne fusionnez pas des faits de granularités différentes dans une même table.
- N'activez pas le filtrage bidirectionnel pour « faire fonctionner » un visuel : chemins ambigus et performances dégradées. Source
- N'utilisez pas une colonne d'une table de faits comme axe ou segment : elle ne filtre pas les autres tables de faits.
À retenir
- Une table de faits, une granularité.
- Les tables de faits communiquent uniquement par des dimensions conformes.
- Un fait plus agrégé se relie par le premier jour de la période et une dimension dédiée, ou une relation *:* unidirectionnelle.
- La mesure d'un fait agrégé renvoie BLANK sous sa granularité.
Pour aller plus loin
- Conseils sur les relations plusieurs-à-plusieurs. Source (Microsoft Learn)
- Modélisation dimensionnelle dans l'entrepôt Microsoft Fabric. Source (Microsoft Learn)
- Les fonctions DAX liées aux relations (TREATAS, USERELATIONSHIP, CROSSFILTER) sont détaillées dans la page DAX et relations de la sous-partie Calculer avec DAX.
Exercice
À partir du modèle de vente du support :
- Importez une table d'objectifs mensuels par catégorie (Mois au 1er du mois, Catégorie, Montant objectif).
- Créez la dimension Catégorie ou la relation *:* unidirectionnelle.
- Écrivez les mesures Objectif, Écart à l'objectif et % Atteinte.
- Construisez une matrice Année / Mois × Catégorie et vérifiez qu'au jour ou au produit l'objectif est vide.
- Refaites la mesure Objectif avec TREATAS et comparez les résultats dans l'analyseur de performances.
L'exercice DAX « 6 | Objectifs (TREATAS) » de la sous-partie Calculer avec DAX prolonge ce travail.










