如何在表格模型中计算/存储前 10 名?

Tar*_*ryn 23 ssas tabular-model

我们最近创建了一个 SSAS 表格模型,以便我们的用户可以通过 PowerView 访问它。我们对我们的一个事实表进行了测量,以TotalActiveItems使用公式获取:

TotalActive:=COUNTAX(FILTER('Stats', ISBLANK([DeactDate]) = TRUE), 1)
Run Code Online (Sandbox Code Playgroud)

这在需要时非常有效,但现在我们有一个请求,以获取TotalActive.

作为参考,这是我们模型的一部分:

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
);
Run Code Online (Sandbox Code Playgroud)

SQL 摆弄表格和示例数据。

factStats表有FKS的DevIDCustIDBillDateTimeID,和ParentID。我们的请求是基于计算或存储Top 10 Parents每个BillDateTimeIDTotalActive 并且将不在前 10 名中的所有内容包含在类似于以下的汇总类别中:

+----------------+------------+------+
| BillDateTimeID |   Parent   | Rank |
+----------------+------------+------+
|       20140801 | Jim        |    1 |
|       20140801 | Bob        |    2 |
|       20140801 | All Others |    3 |
+----------------+------------+------+
Run Code Online (Sandbox Code Playgroud)

我可以使用窗口函数在 SQL 中轻松完成此操作,但尝试为 SSAS 重现此操作很困难。在 SQL 中,我们将使用以下方法获得结果:

;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;
Run Code Online (Sandbox Code Playgroud)

SQL 小提琴演示

我尝试了几种不同的方法来获得结果,但每种方法都有问题。我的尝试如下。

最初,我能够使用 MDX 查询在某种程度上获取数据,但后来不知道如何将其合并到我们的表格模型中。供参考的 MDX 查询是:

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]};
Run Code Online (Sandbox Code Playgroud)

当然,这也只给了我一个月的结果,不是每个月。

当我意识到 MDX 查询不起作用时,我开始更改我们的factStats表以包含一个新列来标记前 10 名和汇总值中的项目。

alter table factStats
    add Top10ParentID INT NOT NULL
    constraint DF_factStats default (0);
Run Code Online (Sandbox Code Playgroud)

默认约束引用了前 10 名的“Rolled Up”值。

尝试 #1: 我创建了新的 Top 10 表来存储 ParentID、姓名和排名:

create table dimTop10Parent
(
    Top10ParentID INT NOT NULL PRIMARY KEY,
    ParentName varchar(100) NOT NULL,
    Parent_Rank INT NOT NULL
);
Run Code Online (Sandbox Code Playgroud)

每次我们使用新的前 10 个父项刷新我们的模型时,都会根据它们拥有的总活动项填充此表。Parent_Rank然后该列隐藏在我们的表格模型中,专门用于排序。这很好用,除了我们没有能力在历史上获得前 10 名,因为它不是基于逐月计算的。

尝试 #2:创建一个新表来存储前 10 个,但 PRIMARY KEY 将包括 Top10ParentID 和 BillingDateTimeID。

create table dimTop10Parent
(
    Top10ParentID INT NOT NULL,
    ParentName varchar(100) NOT NULL,
    Parent_Rank INT NOT NULL,
    BillDateTimeID BIGINT NOT NULL
);
Run Code Online (Sandbox Code Playgroud)

这样做的问题是我们无法在表格模型中的 dimTop10Parent 中的 factStats 单个 FK 与两部分 PK 之间创建关系。

尝试 #3:创建新表,但使用身份作为 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
);
Run Code Online (Sandbox Code Playgroud)

factStats表将存储Top10ID每行唯一的值。我认为这会解决我的问题,但没有解决,因为我们无法再按Parent_Rank模型中的排序,它会引发错误:

无法按 Parent_Rank 对 ParentName 进行排序,因为 ParentName 中的至少一个值在 Parent_Rank 中有多个不同的值。例如,您可以按 [地区] 对 [城市] 进行排序,因为每个城市只有一个地区,但不能按 [城市] 对 [地区] 进行排序,因为每个地区有多个城市。

使用示例数据,最终结果应该类似于(这显示了前 2 名和第 3 名的卷起):

| 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 |
Run Code Online (Sandbox Code Playgroud)

在这一点上,我对如何得到这个最终结果感到茫然。我可以根据需要更改表格以获取它,我可以使用公式、度量等更改模型。我已经阅读了有关使用 DAX 公式123 进行排名的文章但我似乎无法理解它们足以能够准确地得到结果。

我如何计算/存储任何月份的前 10 名,并且仍然能够在我们的表格模型中根据需要拼接数据?

小智 1

我有一个类似的场景并使用了以下 DAX 查询...

首先,为了简单起见,我定义了一个在 DAX 内部使用的度量,这样我就不必重复公式。然后我使用生成来迭代 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
Run Code Online (Sandbox Code Playgroud)

通过上述内容,您应该获得前 10 个 ParentID 和每个月的度量。只需将“TableInTabular”替换为包含数据的表格名称,将“DatesTableName”替换为日期表的名称。

如果我误解了您的问题,请告诉我并希望它有所帮助......