【问题标题】:SQL Rollup equivalent in MDXMDX 中的 SQL 汇总等效项
【发布时间】:2017-05-19 10:56:36
【问题描述】:

我有一种情况,我想查询多个属性(总共约 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() 函数的第二个参数来减小交叉连接集的大小?这不会帮助您解决“像 WITH ROLLUP 这样的选择性总计”问题,但至少可以使交叉连接成为可能。

标签: sql-server ssas mdx rollup


【解决方案1】:

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

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

有一种方法可以让 MDX 知道这个顺序。它有点复杂,但可能比您尝试的长期“手工构建”方法更容易(或性能更好):

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(根本没有 ALL 值),要么是与自身长度一样长的 1 字符串。

(请注意,我的示例中没有包含任何 NonEmpty 内容,或者您​​实际想要查看的度量)。

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

【讨论】:

  • 忘记在我的示例中包含非空(虽然我没有包含第二个参数,所以这可能会有所帮助)。我认为这就是抱怨 40 亿个元组的原因,所以你的答案中的解决方案可能是要走的路 - 它非常聪明!如果它有效,我会回到这里接受:)
  • 嗯,它几乎可以工作 - 我成功地工作了 5 层,但不仅如此,它还开始抱怨 The Auto Exist operation is exceeding the permitted memory limit and is being canceled. 除非我在周末得到更好的回应,否则我会接受您的答案,因为它在技术上可以完成工作(并且在语法上比我的问题中的长手更好)
  • 听起来这可能只是交叉连接集的大小问题,与我们尝试从中获得类似 ROLLUP 的集无关。如果您只是交叉连接 8 个维度并显示度量,而不需要任何聪明的东西,它是否有效?
  • 如果我包含 all 成员,则不会 - 仅与孩子一起。然而,这极大地减小了大小,这就是为什么我一直在寻找一种可以在内部执行“技巧”的解决方案,而不是作为查询后过滤器。
  • 我明白了——就像 SQL GROUP BY 只查看实际存在的内容,然后进行 ROLLUP,完全避免了巨大的交叉连接集。我怀疑 SSAS 的内部 AutoExists 功能运行得非常好 - 只要您包含所有成员,这会将操作变成更接近完全交叉连接的操作。以前从未想过这个。
【解决方案2】:

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

【讨论】:

  • 这很酷 - 我们最终采用了长期联合方法,因为我们可以从中获得适度的性能 - 我会在其中尝试一下,看看它是否会提高性能,因为那会很有帮助。它不允许我们在不编写大量查询的情况下执行此操作,但它看起来越来越像是不会发生的。谢谢!
  • 看起来这并没有显着提高性能 - 我不知道为什么,但我想这是一个情境问题。
  • 我在问题中包含了最新版本 - 尽管我换掉了所有维度/属性名称
  • 请仔细阅读我的博文。我已经提到了属性数量的参数。你能告诉我完整的代码吗?这可能是您真正的查询,但重要的是要从乞求到结尾,包括选择部分。尝试减少属性数量并测试性能。我看到两个问题: 1. 你的 MDX 又不是很好,你错过了一些东西。 2. 查询结果巨大,不可能。
  • 查询本身实际上只是SELECT [Measures].[X] on Columns, ... on Rows from [Cube] - 除了这个之外,它在任何方面都不是一个棘手的查询。我得出的结论是,对于如此庞大的数据集,这是不可行的。减少属性数量确实会对性能产生相当直接的影响(使用多种不同的技术),所以我认为我在这里过于乐观了。
【解决方案3】:

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

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

相当古怪的是,SubCube 应该与您正在使用的立方体/透视图具有相同的名称。

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

之后放下子立方体。

【讨论】:

    猜你喜欢
    • 2016-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多