Comment calculer / stocker le Top 10 dans un modèle tabulaire?

23

Nous avons récemment créé un modèle tabulaire SSAS afin que nos utilisateurs puissent y accéder via PowerView. Nous avons une mesure sur l'une de nos tables de faits pour obtenir l' TotalActiveItemsaide d'une formule:

TotalActive:=COUNTAX(FILTER('Stats', ISBLANK([DeactDate]) = TRUE), 1)

Cela fonctionne très bien au besoin, mais nous avons maintenant une demande pour obtenir les 10 meilleurs parents pour chaque mois dans le TotalActive.

Pour référence, voici une partie de notre modèle:

create table factStats
(
    StatsID INT IDENTITY NOT NULL PRIMARY KEY,
    DevID INT NOT NULL,
    DeactDate DATETIME NULL,
    BillDateTimeID BIGINT NOT NULL,
    CustID INT NOT NULL,
    ParentID INT NOT NULL
);

create table dimCust
(
    CustID INT NOT NULL PRIMARY KEY,
    CustName varchar(150) NOT NULL
);

create table dimParent
(
    ParentID INT NOT NULL PRIMARY KEY,
    ParentName varchar(100) NOT NULL
);

create table dimDateTime
(
    DateTimeID BIGINT NOT NULL PRIMARY KEY
);

SQL Fiddle avec des tables et des exemples de données.

La factStatstable a FKs au DevID, CustID, BillDateTimeIDet ParentID. La demande que nous avons est de calculer ou de stocker le Top 10 Parentspour chacun en BillDateTimeIDfonction du TotalActive ET et d' inclure tout ce qui n'est pas dans le Top 10 dans une catégorie cumulée similaire à la suivante:

+----------------+------------+------+
| BillDateTimeID |   Parent   | Rank |
+----------------+------------+------+
|       20140801 | Jim        |    1 |
|       20140801 | Bob        |    2 |
|       20140801 | All Others |    3 |
+----------------+------------+------+

Je peux facilement accomplir cela en SQL en utilisant des fonctions de fenêtrage mais essayer de reproduire cela pour SSAS a été difficile. En SQL, nous obtenions le résultat en utilisant:

;with Total as
(
  select 
    ParentID,
    BillDateTimeID,
    sum(case when DeactDate is null then 1 else 0 end) TotalActive
  from factStats
  group by ParentID, BillDateTimeID
),
PRank as
(
  select 
    ParentID,
    BillDateTimeID,
    TotalActive,
    row_number() over(partition by BillDateTimeID 
                      order by TotalActive desc) pr
  from total
)
select 
  parentid,
  BillDateTimeID,
  TotalActive,
  pr
from prank
where pr <= 2
union all
select 
  0,
  BillDateTimeID,
  sum(TotalActive) TotalActive,
  3
from prank
where pr > 2
group by BillDateTimeID
order by BillDateTimeID desc, pr;

Démo SQL Fiddle .

J'ai essayé plusieurs façons d'obtenir le résultat, mais chacune a eu un problème. Mes tentatives sont ci-dessous.

Initialement, j'ai pu obtenir un peu les données à l'aide d'une requête MDX, mais je n'avais aucune idée de la façon de l'incorporer dans notre modèle tabulaire. La requête MDX pour référence est:

with 
set [Top10Parent] AS
(
    (TOPCOUNT({ORDER(({[Parent].[Parent Name].[Parent Name]}),
        ([Measures].[Total Count]), BDESC)}, 10))
)
MEMBER [Parent].[Parent Name].[Others] AS
(
    AGGREGATE(EXCEPT([Parent].[Parent Name].[Parent Name], [Top10Parent]))
)
select 
    [Measures].[Total Count] on columns,
    {[Top10Parent]}+ {[Parent].[Parent Name].[Others]} on Rows
from [OurModel]
where {[Date and Time].[Month and Year].[Month and Year].[Jul 2014]};

Bien sûr, cela ne m'a également donné le résultat que pour un seul mois, pas tous les mois.

Quand j'ai réalisé que la requête MDX ne fonctionnerait pas, j'ai commencé par modifier notre factStatstable pour inclure une nouvelle colonne pour marquer les éléments dans le Top 10 et dans la valeur cumulée.

alter table factStats
    add Top10ParentID INT NOT NULL
    constraint DF_factStats default (0);

La contrainte par défaut fait référence à notre valeur "Rolled Up" pour le Top 10.

Tentative n ° 1: j'ai créé une nouvelle table Top 10 pour stocker le ParentID, le nom et le rang:

create table dimTop10Parent
(
    Top10ParentID INT NOT NULL PRIMARY KEY,
    ParentName varchar(100) NOT NULL,
    Parent_Rank INT NOT NULL
);

Ce tableau sera ensuite rempli chaque fois que nous actualiserons notre modèle avec les nouveaux 10 meilleurs parents en fonction du nombre total d'éléments actifs dont ils disposent. La Parent_Rankcolonne est ensuite masquée dans notre modèle tabulaire et utilisée exclusivement pour le tri. Cela fonctionne très bien, sauf que nous n'avons pas la possibilité d'obtenir historiquement le Top 10 car il n'est pas basé sur un mois en mois.

Tentative n ° 2: créez une nouvelle table pour stocker le Top 10, mais la PRIMARY KEY inclura à la fois le Top10ParentID et le BillingDateTimeID.

create table dimTop10Parent
(
    Top10ParentID INT NOT NULL,
    ParentName varchar(100) NOT NULL,
    Parent_Rank INT NOT NULL,
    BillDateTimeID BIGINT NOT NULL
);

Le problème avec cela est que nous ne pouvons pas créer une relation entre le FK unique factStats et le PK en deux parties dans le dimTop10Parent dans le modèle tabulaire.

Tentative n ° 3: créez la nouvelle table mais utilisez une identité comme PK.

create table dimTop10Parent
(
    Top10ID INT IDENTITY NOT NULL PRIMARY KEY,
    Top10ParentID INT NOT NULL,
    ParentName varchar(100) NOT NULL,
    Parent_Rank INT NOT NULL,
    BillDateTimeID BIGINT NOT NULL
);

Le factStatstableau stockera la Top10IDvaleur qui sera unique pour chaque ligne. Je pensais que cela résoudrait mon problème, mais ce n'est pas le cas car nous ne pouvons plus trier par Parent_Rankdans le modèle, cela génère une erreur:

Impossible de trier ParentName par Parent_Rank car au moins une valeur dans ParentName a plusieurs valeurs distinctes dans Parent_Rank. Par exemple, vous pouvez trier [Ville] par [Région] car il n'y a qu'une seule région pour chaque ville, mais vous ne pouvez pas trier [Région] par [Ville] car il y a plusieurs villes pour chaque région.

En utilisant les données de l'échantillon, le résultat final devrait être similaire à (cela montre le Top 2 avec un 3e enroulé):

| PARENTNAME | BILLDATETIMEID | TOTALACTIVE | PR |
|------------|----------------|-------------|----|
|     FDN    |   201408010000 |          11 |  1 |
|     FDO    |   201408010000 |           3 |  2 |
| All Others |   201408010000 |           5 |  3 |
|     FDN    |   201407010000 |          12 |  1 |
|     EVOD   |   201407010000 |           2 |  2 |
| All Others |   201407010000 |           5 |  3 |

À ce stade, je ne sais pas comment obtenir ce résultat final. Je peux modifier les tables au besoin pour l' obtenir, je peux modifier le modèle en utilisant une formule, mesure, etc. J'ai lu sur le classement à l' aide DAX formules 1 , 2 , 3 , mais je ne peux pas sembler ma tête envelopper suffisamment pour pouvoir obtenir le résultat avec précision.

Comment puis-je calculer / stocker ce Top 10 pour n'importe quel mois et être toujours en mesure d'épisser les données selon les besoins dans notre modèle tabulaire?

Taryn
la source

Réponses:

1

J'ai eu un scénario similaire et j'ai utilisé la requête DAX suivante ...

Tout d'abord, pour faire simple, j'ai défini une mesure à utiliser à l'intérieur du DAX, donc je n'ai pas à répéter la formule. Ensuite, j'ai utilisé le générer pour itérer sur la formule TOPN:

define measure TableInTabular[NameOfTheMeasure] = COUNTAX(FILTER('Stats', ISBLANK([DeactDate]) = TRUE), 1)
evaluate
 (
  addcolumns
   (  
    filter
     (  
      generate
        (  
         VALUES(DatesTableName[Month]),  
         TOPN (10, VALUES(TableInTabular[ParentID]),TableInTabular[NameOfTheMeasure],0)
        ),
        TableInTabular[NameOfTheMeasure]>0
      ),
      "ActiveCount (or how you want to call this Column)",
      TableInTabular[NameOfTheMeasure]  
    )  
 )  
order by DatesTableName[Month] asc, 
TableInTabular[NameOfTheMeasure] desc

Avec ce qui précède, vous devriez avoir un top 10 ParentID et la mesure chaque mois. remplacez simplement le "TableInTabular" par le nom de votre tableau tabulaire où vous avez les données et le "DatesTableName" par le nom du tableau des dates.

Veuillez me faire savoir si j'ai mal compris votre question et j'espère que cela vous aidera ...

Alejandro Pelc
la source
1
Merci pour la réponse, il y a encore quelques problèmes avec cela. Tout d'abord, je peux l'utiliser dans SSMS, mais cela est déployé dans notre modèle tabulaire afin que nos utilisateurs puissent y accéder via PowerView - ils n'écriront aucune requête - cela doit simplement être disponible. Deuxièmement, à moins que je fasse quelque chose de mal, il n'y a pas d'évaluation ou d'ordre autorisé dans le modèle tabulaire via Visual Studio - aucune option pour cela en tant que fonction. Troisièmement, cette requête ne renvoie que le Top 10, j'ai également besoin des données cumulées ou d'un moyen pour les obtenir. Je vais continuer à jouer avec ça.
Taryn