MDX - TopCount 加上 'Other' 或 'The Rest' 按组(在一组成员上)

MDX - TopCount plus 'Other' or 'The Rest' by group (over a set of members)

我需要按客户组显示前 5 位客户的销售额,但该组内其他客户的销售额汇总为 'Others'。类似于 this question,但针对每个客户组单独计算。

根据 MSDN,要对一组成员执行 TopCount,您必须使用 Generate 函数。

这部分工作正常:

with 

set [Top5CustomerByGroup] AS
GENERATE
( 
    [Klient].[Grupa Klientow].[Grupa Klientow].ALLMEMBERS,
    TOPCOUNT
    (
        [Klient].[Grupa Klientow].CURRENTMEMBER * [Klient].[Klient].[Klient].MEMBERS
        , 5
        , [Measures].[Przychody ze sprzedazy rzeczywiste wartosc]
    )
)

SELECT 
{ [Measures].[Przychody ze sprzedazy rzeczywiste wartosc]} ON COLUMNS,
{
[Klient].[Grupa Klientow].[Grupa Klientow].ALLMEMBERS * [Klient].[Klient].[All], --for drilldown purposes
[Top5CustomerByGroup]
}
ON ROWS
FROM 
(
  SELECT ({[Data].[Rok].&[2013]} ) ON COLUMNS
      FROM [MyCube]
)

但是 'Others' 部分有问题。

我认为我能够按组与其他客户构建集合(数据看起来不错)作为:

set [OtherCustomersByGroup] AS
GENERATE
( 
    [Klient].[Grupa Klientow].[Grupa Klientow].ALLMEMBERS,
    except
    (
        {[Klient].[Grupa Klientow].CURRENTMEMBER * [Klient].[Klient].[Klient].MEMBERS},
        TOPCOUNT
        (
            [Klient].[Grupa Klientow].CURRENTMEMBER * [Klient].[Klient].[Klient].MEMBERS
            , 5
            , [Measures].[Przychody ze sprzedazy rzeczywiste wartosc]
        )
    )
)

但是我不知道如何通过分组来聚合它。

按照 this question

中的方式进行操作
member [Klient].[Klient].[tmp] as
aggregate([OtherCustomersByGroup])

产生一个值,这是合乎逻辑的。

我想我需要每个组中有 'other' 个客户的集合列表,而不是单个 [OtherCustomersByGroup] 集合,但不知道如何构造它们。

大家有什么想法或建议吗?

更新:

对我的需求有些误解。我需要每个客户组中的前 n 个客户按销售额与该组中其他客户的销售额汇总到一个位置(假设称为其他)。

例如这个简化的输入:

| Group  | Client   | Sales  |
|--------|----------|--------|
| Group1 | Client1  |    300 |
| Group1 | Client2  |      5 |
| Group1 | Client3  |    400 |
| Group1 | Client4  |    150 |
| Group1 | Client5  |    651 |
| Group1 | Client6  | null   |
| Group2 | Client7  |     11 |
| Group2 | Client8  |     52 |
| Group2 | Client9  |     44 |
| Group2 | Client10 |     21 |
| Group2 | Client11 |    201 |
| Group2 | Client12 |    325 |
| Group2 | Client13 |    251 |
| Group3 | Client14 |     15 |

我需要这样的输出(这里是前 2 个):

| Group  | Client   | Sales  |
|--------|----------|--------|
| Group1 | Client5  |    651 |
| Group1 | Client3  |    400 |
| Group1 | Others   |    455 |
| Group2 | Client12 |    325 |
| Group2 | Client13 |    251 |
| Group2 | Others   |    329 |
| Group3 | Client14 |     15 |
| Group3 | Others   |  null  | <- optional row

不需要排序,我们会在客户端处理。

是的,您已经通过使用 SET for Others 了解了主要思想,但需要进行一些小的补充才能完成任务。

我将使用我的测试数据库,但这可以很容易地转换为您的。

  • [Report Date] - 日期维度([Klient] 模拟)
  • [REPORT DATE Y] - 年层次结构 ([Grupa Klientow])
  • [REPORT DATE YM] - 月份层次结构 ([Klient].[Klient])
  • [Measures].[Count] - TopCount ([Measures].[Przychody ze sprzedazy rzeczywiste wartosc])
  • 的度量

我也用了top 3只是为了在这里显示结果图像。

这是代码:

with

/* first, add empty [Other] member to the group level */
member [Report Date].[REPORT DATE Y].[Other] as null

/* second, copy measure by fixing the lowest level */
member [Measures].[Count with Other Groups] as ([Report Date].[REPORT DATE YM],[Measures].[Count])

/* third, create top 10 by group */
set [Report Date Top 10 Groups] as
Generate([Report Date].[REPORT DATE Y].Children
,TopCount([Report Date].[REPORT DATE Y].CurrentMember
 * [Report Date].[REPORT DATE YM].Children,3,[Measures].[Count with Other Groups]))

/* this is the part for Other group mapping */
set [Report Date Other Groups] as
[Report Date].[REPORT DATE Y].[Other]
 * ([Report Date].[REPORT DATE YM].Children
    - Extract([Report Date Top 10 Groups],[Report Date].[REPORT DATE YM]))

select {[Measures].[Count],[Measures].[Count with Other Groups]} on 0
,
{
[Report Date Top 10 Groups],[Report Date Other Groups]}
on 1
from 
[DATA]

结果如下:

..所有成员直到最后一个(即 201606)都在 Other 组中。

希望这对您有所帮助,bardzo dziękuję!

更新: 代码通过在 Report Date Other Groups 计算中删除一个乘法进行了优化。

Update-2:(尚未解决,但正在进行中)

(每个组下使用'Other'个成员)

重要!我们需要额外的层次结构:Group->Client[Report Date].[REPORT DATE]Year->Month 是我的情况)能够确定每个低级别成员的父级。

with

/* create top 10 by group */
set [Report Date Top 10 Groups] as
Generate([Report Date].[REPORT DATE Y].Children
,TopCount([Report Date].[REPORT DATE Y].CurrentMember
 * [Report Date].[REPORT DATE].Children,3,[Measures].[Count]))

/* this is the part for Other group the lowest level non-aggregated members */
set [Report Date Other Members] as
[Report Date].[REPORT DATE Y].Children
* ([Report Date].[REPORT DATE].[Month].AllMembers
    - [Report Date].[REPORT DATE].[All])
- [Report Date Top 10 Groups]

/* add empty [Other] member to the group level, HERE IS AN ISSUE */
member [Report Date].[REPORT DATE].[All].[Other] as null

set [Report Date Other Groups] as
[Report Date].[REPORT DATE Y].[All].Children
* [Report Date].[REPORT DATE].[Other]

member [Measures].[Sum of Top] as
IIF([Report Date].[Report Date].CurrentMember is [Report Date].[REPORT DATE].[Other]
,null /* HERE SHOULD BE CALCULATION, but only
 {[Report Date].[Report Date Y].[All].[Other]}
 is shown, because 'Other' is added to the entire hierarchy */
,SUM([Report Date].[REPORT DATE Y].CurrentMember
        * ([Report Date].[Report Date].CurrentMember.Parent.Children
            - Extract([Report Date Other Members],[Report Date].[REPORT DATE]))
    ,[Measures].[Count]))

member [Measures].[Sum of Group] as
([Report Date].[Report Date].CurrentMember.Parent,[Measures].[Count])

select {[Measures].[Count],[Measures].[Sum of Group],[Measures].[Sum of Top]} on 0
,
Order(Hierarchize({[Report Date Top 10 Groups]
,[Report Date Other Groups]}),[Measures].[Count],DESC)

on 1
from 
[DATA]

这是中间结果:

我需要把这个结果移到这里,但不知道该怎么做。

我也尝试过使用每个级别的平面层次结构。 Other 成员显示正确,但无法计算 SUM,因为两个级别是独立的。也许我们可以添加一个 属性 像 'Group_Name' 并使用未链接的关卡,但同样 - 它会大大降低性能。所有这些IIF([bla-bla-bla low level member].Properties("Group_Name")=[bla-bla-bla group level].Member_Name 都非常慢。

Update-3(上述代码的 AdvWorks 版本)

with

/* create top 10 by group */
set [Top 10 Groups] as
Generate([Customer].[Country].Children
,TopCount([Customer].[Country].CurrentMember
 * [Customer].[Customer Geography].Children,3,[Measures].[Internet Order Count]))

/* this is the part for Other group the lowest level non-aggregated members */
set [Other Members] as
[Customer].[Country].Children
* ([Customer].[Customer Geography].[State-Province].AllMembers
    - [Customer].[Customer Geography].[All])
- [Top 10 Groups]

/* add empty [Other] member to the group level */
member [Customer].[Customer Geography].[All].[Other] as
([Customer].[Country],[Measures].[Internet Order Count])

set [Other Groups] as
[Customer].[Country].[All].Children
* [Customer].[Customer Geography].[Other]

member [Measures].[Sum of Top] as
IIF([Customer].[Customer Geography].CurrentMember is [Customer].[Customer Geography].[Other]
,null
,SUM([Customer].[Country].CurrentMember
        * ([Customer].[Customer Geography].CurrentMember.Parent.Children
            - Extract([Other Members],[Customer].[Customer Geography]))
    ,[Measures].[Internet Order Count]))

member [Measures].[Sum of Group] as
([Customer].[Customer Geography].CurrentMember.Parent,[Measures].[Internet Order Count])

select {[Measures].[Internet Order Count],[Measures].[Sum of Group],[Measures].[Sum of Top]} on 0
,
Order(Hierarchize({[Top 10 Groups],[Other Groups]}),[Measures].[Internet Order Count],DESC) on 1
from [Adventure Works]

Update-4(在year/month示例中有一个解决方案)

@whytheq 的惊人解决方案帮助完成了我想要的事情:

WITH 
  SET [All Grupa Klientow]  AS ([Report Date].[Report Date Y].Children) 
  SET [All Klient] AS ([Report Date].[Report Date YM].Children)
  SET [Top N Members] AS 
    Generate
    (
      [All Grupa Klientow]
     ,TopCount
      (
        (EXISTING 
          [All Klient])
       ,3
       ,[Measures].[Count]
      )
    ) 
  MEMBER [Report Date].[Report Date YM].[Other] AS 
    Aggregate({(EXISTING {[All Klient]} - [Top N Members])}) 
SELECT 
  {[Measures].[Count]} ON 0
 ,{
      [All Grupa Klientow]
    * 
      {
        [Top N Members]
       ,[Report Date].[Report Date YM].[Other]
      }
  } ON 1
FROM [DATA];

和图片:

任务已解决,但请标记不是这个答案,而是@whytheq 的!

以下内容反对 AdvWrks,并使用了我在 Chris Webb 的博客上看到的技术,他在此处概述了该技术:
https://cwebbbi.wordpress.com/2007/06/25/advanced-ranking-and-dynamically-generated-named-sets-in-mdx/

我发现创建集合 MyMonthsWithEmployeesSets 的脚本部分很难理解 - 也许@AlexPeshik 可以更清楚地说明以下脚本中发生的事情。

WITH 
  SET MyMonths AS 
    TopPercent
    (
      [Date].[Calendar].[Month].MEMBERS
     ,20
     ,[Measures].[Reseller Sales Amount]
    ) 
  SET MyEmployees AS 
    [Employee].[Employee].[Employee].MEMBERS 
  SET MyMonthsWithEmployeesSets AS 
    Generate
    (
      MyMonths
     ,Union
      (
        {[Date].[Calendar].CurrentMember}
       ,StrToSet
        ("
             Intersect({}, 
             {TopCount(MyEmployees, 10, ([Measures].[Reseller Sales Amount],[Date].[Calendar].CurrentMember))
             as EmployeeSet"
            + 
              Cstr(MyMonths.CurrentOrdinal)
          + "})"
        )
      )
    ) 
  MEMBER [Employee].[Employee].[RestOfEmployees] AS 
    Aggregate
    (
      Except
      (
        MyEmployees
       ,StrToSet
        (
          "EmployeeSet" + Cstr(Rank([Date].[Calendar].CurrentMember,MyMonths))
        )
      )
    ) 
  MEMBER [Measures].[EmployeeRank] AS 
    Rank
    (
      [Employee].[Employee].CurrentMember
     ,StrToSet
      (
        "EmployeeSet" + Cstr(Rank([Date].[Calendar].CurrentMember,MyMonths))
      )
    ) 
SELECT 
  {
    [Measures].[EmployeeRank]
   ,[Measures].[Reseller Sales Amount]
  } ON 0
 ,Generate
  (
    Hierarchize(MyMonthsWithEmployeesSets)
   ,
      [Date].[Calendar].CurrentMember
    * 
      {
        Order
        (
          Filter
          (
            MyEmployees
           ,
            [Measures].[EmployeeRank] > 0
          )
         ,[Measures].[Reseller Sales Amount]
         ,BDESC
        )
       ,[Employee].[Employee].[RestOfEmployees]
      }
  ) ON 1
FROM [Adventure Works];

编辑 - Alex 第三次尝试的解决方案:

WITH 
  SET [AllCountries] AS [Country].[Country].MEMBERS 
  SET [AllStates]    AS [State-Province].[State-Province].MEMBERS 
  SET [Top2States] AS 
    Generate
    (
      [AllCountries]
     ,TopCount
      (
        (EXISTING 
          [AllStates])
       ,3
       ,[Measures].[Internet Order Count]
      )
    ) 
  MEMBER [State-Province].[All].[RestOfCountry] AS 
    Aggregate({(EXISTING {[AllStates]} - [Top2States])}) 
SELECT 
  {[Measures].[Internet Order Count]} ON COLUMNS
 ,{
      [AllCountries]
    * 
      {
        [Top2States]
       ,[State-Province].[All].[RestOfCountry]
       ,[State-Province].[All]
      }
  } ON ROWS
FROM [Adventure Works];