SQL MDX 中的汇总等效项

SQL Rollup equivalent in MDX

我有一种情况想要查询多个属性(总共 ~8 个)并包括小计。这就是我想要的结果:

╔═══════╦═════════╦════════╦═════════╗
║ Attr1 ║  Attr2  ║ Attr3  ║ Measure ║
╠═══════╬═════════╬════════╬═════════╣
║ All   ║ All     ║ All    ║ 50%     ║
║ Foo   ║ All     ║ All    ║ 25%     ║
║ Bar   ║ All     ║ All    ║ 90%     ║
║ Foo   ║ Anna    ║ All    ║ 42%     ║
║ Foo   ║ Brian   ║ All    ║ 12%     ║
║ Bar   ║ Charles ║ All    ║ 10%     ║
║ Bar   ║ Dory    ║ All    ║ 112%    ║
║ Foo   ║ Anna    ║ Box    ║ 58%     ║
║ Foo   ║ Anna    ║ Circle ║ 13%     ║
║ ...   ║ ...     ║ ...    ║ ...     ║
╚═══════╩═════════╩════════╩═════════╝

现在,我几乎可以通过执行以下操作到达那里:

select
    {[Measures].[Measure]} on columns,
    nonempty({
        [Dim1].[Attr1].allmembers *
        [Dim2].[Attr2].allmembers *
        [Dim3].[Attr3].allmembers
    }) on rows
from [Cube]

然而,这当然让我得到了一个包含如下成员的集合:

╔═══════╦═════════╦════════╦═════════╗
║ Attr1 ║  Attr2  ║ Attr3  ║ Measure ║
╠═══════╬═════════╬════════╬═════════╣
║ Foo   ║ All     ║ Box    ║ 25%     ║
║ Bar   ║ All     ║ Circle ║ 90%     ║
║ Foo   ║ Anna    ║ Box    ║ 16%     ║
║ Bar   ║ Charles ║ Circle ║ 78%     ║
║ ...   ║ ...     ║ ...    ║ ...     ║
╚═══════╩═════════╩════════╩═════════╝

我不想要 - 我可以接受他们,除了 8 个维度会让交叉连接变得有点疯狂(它给了我一个超过 40 亿的集合的错误其中的元组......)。现在,如果我写 SQL 我可以做一些简单的事情,比如:

select
    Dim1.Attr1,
    Dim2.Attr2,
    Dim3.Attr3,
    Sum(Measures.Measure) as Measure
group by 
    Dim1.Attr1,
    Dim2.Attr2,
    Dim3.Attr3
with rollup

但我找不到在 MDX 中重现它的简单方法。我可以用这样的东西手动构建每个汇总级别:

select
    {[Measures].[Measure]} on columns,
    nonempty(
        {
            {[Dim1].[Attr1].[All]} *
            {[Dim2].[Attr2].[All]} *
            {[Dim3].[Attr3].[All]}
        } +
        {
            {[Dim1].[Attr1].[Attr1].allmembers} *
            {[Dim2].[Attr2].[All]} *
            {[Dim3].[Attr3].[All]}
        } +
        {
            {[Dim1].[Attr1].[Attr1].allmembers} *
            {[Dim2].[Attr2].[Attr2].allmembers} *
            {[Dim3].[Attr3].[All]}
        } +
        {
            {[Dim1].[Attr1].[Attr1].allmembers} *
            {[Dim2].[Attr2].[Attr2].allmembers} *
            {[Dim3].[Attr3].[Attr3].allmembers}
        }
    ) on rows
from [Cube]

但是这已经变得单调乏味了,只有三个维度 - 指定其中的 9 组将是令人讨厌的。那么 - 有没有一种方法可以在 MDX 中简洁地执行此操作,还是我只需要使用长期解决方案?

根据之前的研究,我遇到了很多答案,比如 this one 说使用 WITH MEMBER 语句来创建总行 - 但这对我来说毫无意义,因为它导致我试图用 allmembers 函数避免的相同交叉连接行为。

编辑: 这是代码的最新(净化)版本,包括@Danylo 对 NonEmptyCrossJoin 的建议:

NON EMPTY {
    NONEMPTYCROSSJOIN(
        {[Dim1].[Attribute].[All]} *
        {[Dim2].[Attribute].[All]} *
        {[Dim3].[Attribute].[All]} *
        {[Dim4].[Attribute].[All]} *
        {[Dim6].[Attribute].[All]} *
        {[Dim7].[Attribute].[All]} *
        {[Dim8].[Attribute].[All]} *
        {[Dim9].[Attribute].[All]} *
         [Dim0].[Attribute].[Attribute].ALLMEMBERS
    ) +
    NONEMPTYCROSSJOIN(
         [Dim1].[Attribute].[Attribute].ALLMEMBERS *
        {[Dim2].[Attribute].[All]} *
        {[Dim3].[Attribute].[All]} *
        {[Dim4].[Attribute].[All]} *
        {[Dim6].[Attribute].[All]} *
        {[Dim7].[Attribute].[All]} *
        {[Dim8].[Attribute].[All]} *
        {[Dim9].[Attribute].[All]} *
         [Dim0].[Attribute].[Attribute].ALLMEMBERS
    ) +
    NONEMPTYCROSSJOIN(
         [Dim1].[Attribute].[Attribute].ALLMEMBERS *
         [Dim2].[Attribute].[Attribute].ALLMEMBERS *
        {[Dim3].[Attribute].[All]} *
        {[Dim4].[Attribute].[All]} *
        {[Dim6].[Attribute].[All]} *
        {[Dim7].[Attribute].[All]} *
        {[Dim8].[Attribute].[All]} *
        {[Dim9].[Attribute].[All]} *
         [Dim0].[Attribute].[Attribute].ALLMEMBERS
    ) +

    ...

}

我看不到使用至少一个交叉连接的方法(尽管将您感兴趣的度量放入 NonEmpty() 函数中 - 请参阅评论 - 可能有助于交叉连接 performance/out-of-memory问题)。

SQL ROLLUP 样式的总计根据 GROUP BY 子句中列的顺序排除了 ALL 和非 ALL 的某些组合。 (在您的示例中,此排除在您的结果集中显示为 ALL 的整齐三角形模式)。 MDX 不会这样做:它并不真正关心交叉连接中集合的顺序。

有一种方法可以让 MDX 知道这个顺序。它有点复杂,但可能比您尝试过的冗长 "handbuilt" 方法更容易(或性能更好):

WITH
MEMBER Measures.DimensionsAllPattern AS
    CASE WHEN [Dimension1].[Hierarchy].CurrentMember.Properties("LEVEL_NUMBER")="0" THEN "1" ELSE "0" END +
    CASE WHEN [Dimension2].[Hierarchy].CurrentMember.Properties("LEVEL_NUMBER")="0" THEN "1" ELSE "0" END +
    CASE WHEN [Dimension3].[Hierarchy].CurrentMember.Properties("LEVEL_NUMBER")="0" THEN "1" ELSE "0" END +
    ... etc up to dimension 8...
MEMBER AllPatternStrNum AS VBA!Cstr(VBA!CLng(Measures.DimensionsAllPattern))

SELECT 
{Measures.DimensionsAllPattern,Measures.AllPatternStrNum} ON 0,
FILTER
    (CROSSJOIN
        ([Dimension1].[Hierarchy].AllMembers,
         [Dimension2].[Hierarchy].AllMembers,
         .... etc
         )
     ,
     (Measures.AllPatternStrNum="0") OR
     (Measures.AllPatternStrNum=VBA!String(VBA!Len(Measures.AllPatternStrNum),"1"))
     )
ON 1
FROM TheCube

这是做什么的:

  1. 对于维度中的每个成员组合,根据指定的维度顺序构建与 All/Non-All 模式对应的字符串。例如,{All,Something,All,Something} 将编码为 1010。

  2. 第二个计算成员将此度量值转换为数字,然后再转换回字符串:因此 1010 最终会变成 1010,但 0011 最终会变成 11(去除前导零的简单方法)

  3. 然后根据第二个成员过滤交叉连接集。它必须等于 0(根本没有所有值),或者是与其自身长度一样长的 1 字符串。

(请注意,我的示例中没有包含任何 NonEmpty 内容,也没有包含您真正想要看到的度量)。

您可能需要将过滤后的集合包装在 ORDER(set,something,BASC) 中,以使其看起来像您想要的那样。

我最近发表了一篇关于此的文章。为了避免交叉连接内存问题,我建议使用 NonEmptyCrossJoin 函数包装集合。在这里阅读更多:http://blog.soft-prestidigitation.com/the-totals-and-the-nonemptycrossjoin-function.html

加速此类 MDX 的一种方法是不要一步完成。 SQL也是如此。使用子立方体。

CREATE SUBCUBE [CubeName] AS 
 SELECT {SomeMeasures} ON COLUMNS, 
{ 
    CROSSJOIN({Field1.ALLMEMBERS}, 
             {Field2.ALLMEMBERS},
                  More as needed                
                                 ) }
ON ROWS 
FROM [CubeName]

相当奇怪,SubCube 应该与您正在使用的 cube/perspective 同名。

您可以根据需要创建任意数量的子多维数据集,所有子多维数据集都具有相同的名称,然后从缩小的多维数据集中执行最终的 SELECT 语句。

之后放下子立方体。