理解传递次序和求解次序 (MDX)

当某个多维数据集是 MDX 脚本的计算结果时,该多维数据集可能会经历许多计算阶段,这要取决于与计算有关的各种功能的使用情况。每个阶段称为一个计算传递。

计算传递可以按序号位置(称为“计算传递号”)进行引用。完全计算一个多维数据集中的所有单元所需的计算传递数称为多维数据集的计算传递深度。

事实数据表和写回数据仅影响传递 0。脚本将在传递 0 后填充数据,脚本中的每一个赋值和计算语句都将创建一个新的传递。在 MDX 脚本外,对绝对传递 0 的引用将引用脚本为多维数据集创建的最后一个传递。

在所有传递中都会创建计算成员,但表达式只应用于当前传递。之前的传递包含计算度量值,但是值为空。

求解次序

求解次序决定了出现相互竞争的表达式时的计算优先级。在一个传递中,求解次序决定了两点:

  • MicrosoftSQL ServerAnalysis Services 计算维度、成员、计算成员、自定义汇总和计算单元的次序。

  • Analysis Services 计算自定义成员、计算成员、自定义汇总和计算单元的次序。

具有最高求解次序的成员优先。

注意注意

此优先级的例外情况是聚合函数。与聚合函数一起使用的计算成员的求解次序比任何相交的计算度量值都低。

计算优先级

计算优先级定义为当前单元内的定义表达式获取值的顺序。按照下面的算法解析计算优先级:

  1. 按照当前单元的粒度(或小于该粒度)基于所有计算创建计算列表 (CL)

  2. 将 CL 中的计算分类到:最高者优先 (HW) 或最接近者优先 (CW)

    1. CW 是自定义汇总、一元运算符、半累加性度量值和冻结

    2. HW 是其他所有计算

  3. 基于按传递排序的所有 HW 创建顺序计算列表 (OCL)

  4. 对于其余的每一个 CW

    1. 对于 OCL 中的每一个计算 (C)(从最高到最低)

    2. 如果 CW 比 C 更接近于当前单元,则在 OCL 中插入 CW 并继续下一个 CW

    3. 如果 CW 比 C 位于更高的传递,则在 OCL 中插入 CW 并继续下一个 CW

    4. 继续下一个 C

  5. 如果 OCL 中的最高计算与当前单元的粒度不同,则使用聚合函数计算聚合值

求解次序值和优先级

求解次序值的范围为 -8181 到 65535。在此范围内,有些求解次序值对应于特定类型的计算,如下表所示。

计算

求解次序

自定义成员公式

-5119

一元运算符

-5119

直观合计计算

-4096

所有其他计算(如果没有另行指定)

0

强烈建议设置求解次序值时仅使用正整数。如果赋值小于上表中所示的求解次序值,计算传递可能会变得不可预知。例如,计算成员的计算接收的求解次序值小于默认自定义汇总公式值 -5119。这样一个低的求解次序值会导致计算成员在自定义汇总公式之前计算,并产生错误的结果。

创建和更改求解次序

在多维数据集设计器的**“计算”**窗格上,可以通过更改计算顺序来更改计算成员和计算单元的求解次序。

在 MDX 中,可以使用 SOLVE_ORDER 关键字来创建或更改计算成员和计算单元。

求解次序示例

为了说明求解次序可能具有的复杂性,下面的系列 MDX 查询将以两个本身没有求解次序问题的查询开始,然后将这两个查询合并成一个需要求解次序的查询。

查询 1 - 收入与支出之间的差额

对于第一个 MDX 查询,若要查看一年中每半年的收入与支出之间的差额,可构造一个与下面的示例类似的简单 MDX 查询:

WITH
MEMBER [Time].[Year Difference] AS
   [Time].[2nd half] - [Time].[1st half]
SELECT 
   { [Account].[Income], [Account].[Expenses] } ON COLUMNS,
   { [Time].[1st half], [Time].[2nd half], [Time].[Year Difference] } ON ROWS
FROM Financials

在此查询中,只有一个计算成员 Year Difference。因为只有一个计算成员,所以只要多维数据集不使用任何计算成员,求解次序就不是问题。

此 MDX 查询将生成一个类似于下表的结果集。

 

Income

Expenses

1st half

5000

4200

2nd half

8000

7000

Year Difference

3000

2800

查询 2 - 支出后的净收益百分比

对于第二个查询,若要查看一年中每半年扣除支出后的净收益百分比,可使用下面的 MDX 查询:

WITH
MEMBER [Account].[Net Income] AS
   ([Account].[Income],  [Account].[Expenses]) / [Account].[Income]
SELECT
   { [Account].[Income], [Account].[Expenses], [Account].[Net Income] } ON COLUMNS,
   { [Time].[1st half], [Time].[2nd half] } ON ROWS
FROM Financials

与上一个查询类似,此 MDX 查询只有一个计算成员 Net Income,因此也不会有任何求解次序问题。

此 MDX 查询将生成一个类似于下表的略有不同的结果集。

 

Income

Expenses

Net Income

1st half

5000

4200

0.16

2nd half

8000

7000

0.125

第一个查询与第二个查询在结果集方面的差异来自计算成员位置的不同。在第一个查询中,计算成员是 ROWS 轴的一部分,而在第二个查询中,计算成员是 COLUMNS 轴的一部分。下一个查询将两个计算成员合并到一个 MDX 查询中,这种位置上的不同在这个查询中就会变得非常重要。

查询 3 - 合并后的年差额和净收益计算

在最后这个查询中,将上面两个示例合并成一个 MDX 查询,这样求解次序就变得重要起来。若要确保按正确的顺序进行计算,请使用 SOLVE_ORDER 关键字定义进行计算的顺序。

SOLVE_ORDER 关键字指定 MDX 查询或 CREATE MEMBER 命令中计算成员的求解次序。SOLVE_ORDER 关键字使用的整数值是相对的,不要求从零开始,也不要求是连续的。该值只是告诉 MDX 基于通过计算具有较大求解次序值的成员得出的值来计算成员。如果不使用 SOLVE_ORDER 关键字定义计算成员,计算成员的默认值将为零。

例如,如果合并前面两个查询示例中使用的计算,则两个计算成员 Year Difference 和 Net Income 将相交于 MDX 查询示例的结果数据集中的一个单元中。确定 Analysis Services 如何计算此单元的唯一方式是通过求解次序。根据两个计算成员的求解次序,用于构造此单元的公式将生成不同的结果。

首先,尝试将前面两个查询中使用的计算合并到下面的 MDX 查询中:

WITH
MEMBER [Time].[Year Difference] AS
   '[Time].[2nd half] - [Time].[1st half],
   SOLVE_ORDER = 1
MEMBER [Account].[Net Income] AS
   '([Account].[Income] - [Account].[Expenses]) / [Account].[Income]',
   SOLVE_ORDER = 2
SELECT
   { [Account].[Income], [Account].[Expenses], [Account].[Net Income] } ON COLUMNS,
   { [Time].[1st half], [Time].[2nd half], [Time].[Year Difference] } ON ROWS
FROM Financials

在这个合并的 MDX 查询示例中,Net Income 具有最高的求解次序,因此当两个表达式会相互影响时,它具有优先权。Analysis Services 使用 Net Income 公式计算相关的单元。此嵌套计算的结果如下表所示。

 

Income

Expenses

Net Income

1st half

5000

4200

0.16

2nd half

8000

7000

0.125

Year Difference

3000

2800

0.066

共享单元中的结果基于 Net Income 所用的公式。也就是说,Analysis Services 使用 Year Difference 数据计算共享单元中的结果,这样就会得到以下公式(为清楚起见,对结果进行了舍入):

((8000 - 5000) - (7000 - 4200)) / (8000 - 5000) = 0.066 

(3000 - 2800) / 3000 = 0.066

但是,如果切换了 MDX 查询中计算成员的求解次序,Analysis Services 将以不同的方式计算共享单元中的结果。下面合并的 MDX 查询反转了计算成员的求解次序:

WITH
MEMBER [Time].[Year Difference] AS
   '[Time].[2nd half] - [Time].[1st half],
   SOLVE_ORDER = 2
MEMBER [Money].[Net Income] AS
   '([Money].[Income] - [Money].[Expenses]) / [Money].[Income]',
   SOLVE_ORDER = 1
SELECT
   { [Money].[Income], [Money].[Expenses], [Money].[Net Income] } ON COLUMNS,
   { [Time].[1st half], [Time].[2nd half], [Time].[Year Difference] } ON ROWS
FROM TestCube

由于已经切换了计算成员的求解次序,因此 Analysis Services 将使用 Year Difference 公式来计算单元,如下表所示。

 

Income

Expenses

Net Income

1st half

5000

4200

0.16

2nd half

8000

7000

0.125

Year Difference

3000

2800

-0.035

因为此查询对 Net Income 数据使用 Year Difference 公式,所以共享单元的公式与以下计算类似:

((8000 - 7000) / 8000) - ((5000 - 4200) / 5000) = -0.035 

0.125 - 0.16 = -0.035

其他注意事项

求解次序可能会成为需要处理的非常复杂的问题,尤其是在具有很多维度而维度涉及计算成员、自定义汇总公式或计算单元的多维数据集中,更是如此。当 Analysis Services 计算 MDX 查询时,Analysis Services 将考虑给定传递中涉及的所有内容的求解次序值,包括 MDX 查询中指定的多维数据集的维度。