【问题标题】:MDX - TopCount plus 'Other' or 'The Rest' by group (over a set of members)MDX - TopCount 加上“其他”或“其余”按组(在一组成员上)
【发布时间】:2015-02-10 16:14:11
【问题描述】:

我需要按客户组显示前 5 位客户销售额,但该组内的其他客户销售额汇总为“其他”。类似于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]
)

但是我对“其他”部分有疑问。

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

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])

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

我认为我需要每个组中包含“其他”客户的集合列表,而不是单个 [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

排序不是必需的,我们将在客户端处理它。

【问题讨论】:

  • “与分组”是什么意思?你想要什么有什么区别,你链接到的另一个问题是做什么的?
  • @TabAlleman 我希望每个客户群都获得前 5 名和“其他”,而不是一位将军。如链接的 MSDN 文章中所述:“Generate 最常见的实际用途是在一组成员上评估复杂的集合表达式,例如 TopCount。以下示例查询显示行中每个日历年的前 10 个产品 [. ..] 请注意,每年都会显示不同的前 10 名,而使用 Generate 是获得此结果的唯一方法。”我还需要一组成员的“其他人”
  • 这是可能的,但相当复杂:我也许可以提供一个 advWrks 脚本
  • @whytheq,希望你能开发出比我的答案更好的东西,因为即使对于单选查询,它也不是添加成员的最佳解决方案,而且还有大量的乘法。
  • @AlexPeshik 我有一个反对 AdvWrks 立方体的 top N of top M with Other member 脚本 - 我可以发布它,但它完全需要重新编写以适应这个问题:我没有时间这样做。

标签: ssas mdx olap


【解决方案1】:

是的,您已经通过使用 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])

我也使用前 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:(尚未解决,但正在进行中)

(在每个组下使用“其他”成员)

重要!我们需要额外的层次结构:Group-&gt;Client(我的情况是[Report Date].[REPORT DATE]Year-&gt;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(以年/月为例)

@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's!

【讨论】:

  • 谢谢,但是我需要相反的东西:每年其他组。你能看看更新吗?在您的示例中,我需要每年的前 3 个月,每年的其他月份分别汇总到其他项目。
  • Update-3 对于在“组总和”列中返回“其他”的行,您的查询返回 3*Total for Country ....这应该显示什么,或者是什么需要吗?
  • 对于Internet Order Count 测量每个大洲的Other 成员可能会显示同一大洲任何成员的Sum of Group - Sum of Top。我只是不知道如何将它移到那里。 Other for Sum of Group 没有任何意义,它只是当前成员的父级,并且意味着 ALL,但对于每个特定国家/地区,此度量有助于计算整个大陆的价值。我还添加了我的 AdvWorks 的图像。
  • 我们只需要将减法移到绿色箭头的位置即可。蓝色 27659 值只是父级,对其他组没有任何意义。但是对于每个成员来说都是有意义的,因为它是前 N 个组的总和(在我的示例中是前 3 个)。
  • @AlexPeshik 好的 - 我有解决方案并将添加到我的答案中(这是假设你会解决它!)
【解决方案2】:

以下内容反对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];

【讨论】:

  • 不幸的是,这不适用于相同的维度(在我的情况下,YearMonth 级别)。 Engine 将它们都视为一个,并且仅选择 TopCount 作为最低级别 :( 我正在尝试使用另一种方法,但仍然存在一些问题。希望明天完成。
  • @AlexPeshik 这个脚本做了它应该做的事情。这是一种方法的说明,而不是对 OP 的直接回答。
  • 是的,我明白,但使用相同的修改方法并没有帮助。可能是因为我脑子不够用。现在我试图避免使用 StrToSet 并只使用快速函数。仅移动到“其他”成员的预先计算值是一个问题。我可以将此值显示为接近年度任何成员的衡量标准,但不知道如何将其移至“其他”。我已经用这个部分解决方案更新了我的答案,希望你能看到瓶颈。
  • 你有剧本的 AdvWrks 版本吗?我想试一试。
  • 抱歉耽搁了,有很多工作要做。这是我回答的 Update-3 中的 AdvWorks 脚本。维度CustomerCustomer Geography 层次结构最高两个级别CountryState-Province
猜你喜欢
  • 2010-10-21
  • 2015-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多