Une dimension tranches d’âge ou comment jouer avec le temps en MDX

Je ne sais pas si vous avez déjà eu envie de gérer une dimension de tranches d’âges (ou d’une quelconque durée) sous SSAS. Intuitivement ce n’est pas très simple car cela fait partie des choses qui doivent être gérées en pur MDX, a fortiori dans un domaine (les dates) ou la moindre erreur peut transformer la requête en gouffre à performances.

Alors voici une petite méthode éprouvée. Encore une fois il ne s’agit pas là de la solution (je suis un garçon assez modeste 🙂 ) mais d’une manière de faire qui me donne toute satisfaction. Je suis bien sur ouvert à toute suggestion d’amélioration.

Première étape: créer sa table de base

Personnellement je la crée dans la DSV: c’est une table technique qui a mon sens n’a pas grand chose à faire dans mon beau Data Mart (mais elle pourrait y être si le coeur vous en dit). Sa structure est simple: un code/libellé de tranche, ainsi que deux attributs entiers supplémentaires, les bornes inférieures et supérieures de l’intervalle.
Voici la requête de construction, basée sur le beau TABLE VALUE CONSTRUCTOR de SQL Server 2008:

SELECT * FROM
(
   VALUES
   (1, '0 - 24 ans', 0, 24),
   (2, '25 - 29 ans', 25, 29),
   (3, '30 - 34 ans', 30, 34),
   (4, '35 - 39 ans', 35, 39),
   (5, '40 - 44 ans', 40, 44),
   (6, '45 - 49 ans', 45, 49),
   (7, '50 - 54 ans', 50, 54),
   (8, '55 - 59 ans', 55, 59),
   (9, '60 ans +', 60, 70)
)
AS t (TrancheAgeId, TrancheAgeLabel, BorneInf, BorneSup)

Comme vous le voyez rien de rare.

Seconde étape: a little MDX

Dans mon cas mon cube est assez simple: j’ai une dimension temps en usage classique (qui référence mon fait) et une dimension temps référencée qui jour le rôle de dimension « Date de naissance ». Les deux ont même structure (si vous connaissez LinkMember, ceci a évidemment un intérêt non négligeable). Ma dimension tranche d’âge est ajoutée aux CubeDimensions mais sans usage, c’est ce que nous appelons une dimension technique (comme le DateTool de Marco Russo par exemple).
Comme je n’aime pas taper des kilomètres de MDX, j’ai essayé de compacter un brin la définition de cette joyeuseté d’assignment.
Le but est pour chaque Tranche d’âge d’aggréger la mesure courante (Aggregate) sur l’intervalle de la tranche d’âge en cours, en prenant bien soin de forcer le set sur lequel on aggrège sur le DefaultMember des tranches d’age (au moment de l’assignment, les tranches sont à null sauf la tranche par défaut (All) qui comprend les données: si on ne spécifie pas cela, le CurrentMember sur le set à aggréger se positionne sur la tranche en cours, qui n’a pas de données).
SCOPE ([Tranche Age].[Tranche Age].[Tranche Age].Members);
   This =
   Aggregate
   (
      [Tranche Age].[Tranche Age].DefaultMember
      *
      ]DebutTranche[
      :
      ]FinTranche[
   )
END SCOPE;
Il reste à calculer ces deux « bornes » de tranche. On peut le faire en effectuant tout d’abord des opérations sur la dimension Temps: le membre bas de la tranche d’âge est le jour courant sur la dimension Temps – vous noterez au passage que la date actuelle dans mon cas est un ClosingPeriod au niveau jour: car mon groupe de mesure est à la granularité mois avec une dimension au jour – moins la borne inférieure de la Tranche d’Age. Ce « moins » (un DateAdd en SQL) se gère très bien avec un ParallelPeriod en MDX. L’intervalle recherché étant un intervalle sur la Date de Naissance, on ramène le membre issu de la dimension Temps sur la dimension Date de Naissance via un LinkMember, autorisé car on a même structure sur les deux dimensions.
ParallelPeriod
(
   [Date Naissance].[Calendrier].[Année],
   [Tranche Age].[Tranche Age].CurrentMember.Properties("Borne Sup"),
   LinkMember
   (
      ClosingPeriod([Date Actuelle].[Calendrier].[Jour]),
      [Date Naissance].[Calendrier]
   )
)

On arrive finalement au bout de script suivant:

SCOPE ([Tranche Age].[Tranche Age].[Tranche Age].Members);
   This =
   Aggregate
   (
      [Tranche Age].[Tranche Age].DefaultMember
      *
      ParallelPeriod
      (
         [Date Naissance].[Calendrier].[Année],
         [Tranche Age].[Tranche Age].CurrentMember.Properties("Borne Sup"),
         LinkMember
         (
             ClosingPeriod([Date Actuelle].[Calendrier].[Jour])
             ,[Date Naissance].[Calendrier]
         )
      )
      :
      ParallelPeriod
      (
         [Date Naissance].[Calendrier].[Année],
         [Tranche Age].[Tranche Age].CurrentMember.Properties("Borne Inf"),
         LinkMember
         (
             ClosingPeriod([Date Actuelle].[Calendrier].[Jour])
             ,[Date Naissance].[Calendrier]
         )
      )
   );
END SCOPE;

Voilà pour la pause MDX du jour, en espérant que cela puisse vous servir.
A bientôt!

Laisser un commentaire