浅谈SSAS计算中MDX性能改进的几个技巧

开发 开发工具
本文提供了一些简单的技巧,可以帮助你在SSAS的计算中表现出最佳的性能。其中一些是在SSAS 2005/2008中新开发的函数。

1.使用半加性度量值替代Calculation

这算是半加性度量值函数的模拟能力之一,但是,有些只在SSAS Enterprise SKU版本中才支持。但是半加性函数操作更快,差不多是MDX脚本两本的速度

2.使用一元操作符替代Calculation

服从分配律一元操作符(与位置顺序无关的操作符)一般来说要比同样作用的赋值操作快两倍。那是不是所有的Calculation都要抽取出来,然后用一元操作符替换呢,答案是否定的。例外就是那些不服从分配律的一元操作符(包括*,/或者数字值)。在这种情况下,才会有可能改进性能。

3.减小集合计算的大小

当计算值是基于集合,使用NONEMPTY函数减小集合的大小。例如,当计算城市间每个产品的销售平均值,这个查询大约耗时1分46秒,在我的机器上:

  1. with member measures.x as avg(existing  
  2. [Product].[Product].[Product].members*[Customer].[Customer Geography].[City].members,  
  3. [Measures].[Internet Sales Amount])  
  4. select [Date].[Calendar].[Month].members on 0,  
  5. non empty [Product].[Subcategory].members on 1  
  6. from [Adventure Works] where measures.x 

但是添加了NONEMPTY,耗时大约39秒:

  1. with member measures.x as avg(existing   
  2. nonempty( [Product].[Product].[Product].members*[Customer].[Customer Geography].[City].members,[Measures].[Internet Sales Amount])  
  3. , [Measures].[Internet Sales Amount])  
  4. select [Date].[Calendar].[Month].members on 0,  
  5. non empty [Product].[Subcategory].members on 1  
  6. from [Adventure Works] where measures.x 

避免赋给cell像0, Null, “N/A”, “-“ 这样的值
之前翻译的文章已经提过了,AS的引擎在排除空行方面是很高效的。下面的查询使用"-"替换了null值,并且non empty不会排出它们:

  1. with member measures.x as  
  2. iif( not isempty([Measures].[Internet Sales Amount]),[Measures].[Internet Sales Amount],"-")  
  3. select descendants([Date].[Calendar].[Calendar Year].&[2004] ) on 0,  
  4. non empty [Customer].[Customer Geography].[Customer].members on 1  
  5. from [Adventure Works]  
  6. where measures.x 

Non empty操作符不能用来格式化的值上,因此要使用格式化字符串的方式来替换null值,但却排除了空的行,耗时大约是一半的时间:

  1. with member measures.x as  
  2. [Measures].[Internet Sales Amount], FORMAT_STRING = "#.00;(#.00);#.00;-" 
  3. select descendants([Date].[Calendar].[Calendar Year].&[2004] ) on 0,  
  4. non empty [Customer].[Customer Geography].[Customer].members on 1  
  5. from [Adventure Works]  
  6. where measures.x 

有的读者可能已经观察到,上面的两个查询其实是不等效的——第二个查询完全排除了空行。

4.计算格式化值的成本

在某些情况下,采用格式化字符串的成本消耗甚至操作了求值本身。这时候就需要对比是否应用格式化属性时两者的执行时间,例如:

  1. select [Measures].[Internet Average Sales Amount] on 0 from [Adventure Works] cell properties value  
  2. 如果不带格式化的结果明显快的话,可以使用下面的语句实现格式化:  
  3. scope([Measures].[Internet Average Sales Amount]);  
  4. FORMAT_STRING(this) = "currency";  
  5. end scope; 

执行这个查询(应用了格式化),然后在决定是否采用吧

5.使用属性

在SSAS 2000中,当要查找一个集合中哪些成员满足条件通常使用filter函数,例如下面查找男性的顾客:

Filter(Customer.name.Name.members, Customer.name.currentmember.properties(“Gender”) = “Male”)

在SSAS 2005/2008中这样的方式就要避免了,取而代之的是添加一个hierarchy属性Customers.Gender :

(Customers.Gender.Male, Measures.Sales)

这个hierarchy属性可以对终端用户隐藏,但是对MDX表达式仍然有效。

下面查询男性客户的平均销售额:

Avg(Customer.Name.Name.members*Customers.Gender.Male, Measures.Sales)

为什么使用.properties会慢呢?因为在获取一个成员的时候每个成员其实都要获取到,这样才能得到property的值,EXISTS更快些是因为直接使用了内部存储结构

6.使用IS比较操作符

当比较成员时,比较对象时使用IS,不要像下面这样:

Iif( [Currency Code].currentmember.Name = “USA”], …)

正确的方法是:

Iif( [Currency Code].currentmember IS [Currency Code].[USA], …)

这是因为***个语句要将成员转型为string,这是要耗时的,而且没有必要

7.使用NONEMPTY函数

NONEMPTY函数(SSAS 2005新增的)在提出元组方面进行了优化,因此下面的查询就不可取了:

Filter(Customer.Name.members, not IsEmpty( ([Measures].[Unit Sales], [Product].[Name].[Xbox])

合适的方式是:

NonEmpty (Customer.Name.members, ([Measures].[Unit Sales], [Product].[Name].[Xbox]))

其实Filter(, Not Isempty()) 和NonEmpty(,) 是等效的,现在引擎已经自动做了优化

另外:

  1. Count(Filter([year and month].[Month],  
  2. Not IsEmpty(([Project Count], [Layer].[Layer].[web]))   
  3. or Not IsEmpty(([Project Count], [Layer].[Layer].[server])))) 

上面的方式也不可取,应该这样:

  1. Count(NonEmpty ([year and month].[Month],{([Project Count],   
  2. [Layer].[Layer].[web]), ([Project Count], [Layer].[Layer].[server])}) 

8.条件计算

IIF的存在可能会破环引擎的优化,因为优化器不能接受在求值过程中的表达式,这个问题可能以后会解决,但是可以采用一些方案绕过这个问题:

9.条件计算和条件域

IIF有时候用于限定应用于集合成员的计算的域,例如:

  1. This =  
  2. IIF([account].[Account Type].currentmember IS [Account].[Account Type].[Flow],   
  3. ,   
  4. )  

上面的做法不推荐,下面可能会获得更好的性能:

  1. Scope ([Account].[Account Type].[Flow]);  
  2. This = ;  
  3. End Scope;  
  4. Scope ([Account].[Account Type].[Account Type].members –   
  5. [Account].[Account Type].[Flow]);  
  6. This = ;  
  7. End Scope; 

10.使用ValidMeasure减少计算求值的空间消耗

在SSAS 2005中,每个度量值组都有一个IgnoreUnrelatedDimensions 属性,这个属性定义了如何处理与度量值不相关的维度 --或者忽略这个维度,或者剔除默认成员上的空值。

在定义计算的时候,不要使用这个behavior而是使用ValidMeasuer函数,例如:

  1. scope(leaves([Time]), [Currency].[Currency].members - [Currency].[Currency].[Currency].[USA]  
  2. Scope [Measures].[store Sales];  
  3. This = iif( isempty(validmeasure([Measures].[Exchange Rate])), null, [Measures].[Store Sales]/validmeasure([Measures].[Exchange Rate]));  
  4. End Scope; 

11.使用Sum或Aggregate替代求和运算,

避免:

Create Member measures.x as

(Sales, Country.USA) + (Sales, Country.Canada) + (Sales, Country.Mexico)…

推荐:

Create Member measures.x as Sum({Country.USA, Country.Canada, Country.Mexico}, Sales)

12.在大数据量Crossjoin中多重的Hierarchy

如果可能,尽量使在同一纬度中的hierarchy在一起。为什么呢?因为在引擎内部有一个exists在进行Crossjoin的相邻的集合之间。如果一个来自不用纬度的hierarchy插在两个属于相同纬度的hierarchy之间,exists的行为会变化,就会增加占用的空间,影响性能

13.缓存计算结果

在公式引擎内部有一个缓存,用于重用计算结果,但是要进行缓存,这个结果在Cube空间中就要有一个可定地址的单元格或者元组。

例如,一个应用程序在集合中定义了一个比率:一个值除***值,并且展示为柱状图。

***种方法:

  1. with   
  2. member measures.y as  
  3. measures.[unit sales]  
  4. /  
  5. max(customers.[name].[name].members, measures.[unit sales])  
  6. select  
  7. measures.y on 0,  
  8. customers.[name].[name].members on 1  
  9. from sales 

这个执行大约需要15秒。这个计算其实效率不高,因为***值其实是不变的,但是每次表达式都要进行求值,下面的查询就要好一些:

  1. with   
  2. member measures.x as   
  3. max(customers.[name].[name].members, measures.[unit sales])  
  4. member measures.y as  
  5. measures.[unit sales]  
  6. /  
  7. (measures.x,[Customers].[Customers].[All Customers] )  
  8. select   
  9. measures.y on 0,  
  10. customers.[name].[name].members on 1  
  11. from sales 

这个查询不到1秒!性能得到了数量级的提升!

不知道读者有没有注意到一个问题?为什么SSAS表达式中的measure.y必须要在分母中包含[Customers].[Customers].[All Customers] ,换句话说为什么不能这样:

  1. with   
  2. member measures.x as   
  3. max(customers.[name].[name].members, measures.[unit sales])  
  4. member measures.y as  
  5. measures.[unit sales]  
  6. /  
  7. (measures.x)  
  8. select   
  9. measures.y on 0,  
  10. customers.[name].[name].members on 1  
  11. from sales  

这是因为我们需要引用缓存值。如果[Customers].[Customers].[All Customers]没有写上customer维度的属性,那么单元格会跟着客户名的引用和计算值变化,而计算值每次都要重新求值

【编辑推荐】

  1. 在T-SQL中使用临时表的注意事项
  2. SQL Server数据库管理常用的SQL和T-SQL语句(1)
  3. 用T-SQL操作面试SQL Server开发人员(1)
  4. SQL Server 2005中的T-SQL
  5. T-SQL实用例句
  6. 微软SQLServer密码管理的危险判断
责任编辑:彭凡 来源: cnblogs
相关推荐

2014-09-17 11:20:38

AndroidListView技巧

2011-07-05 14:59:17

java

2022-04-06 07:32:41

Java运算符变量

2011-06-08 16:52:16

软件测试

2015-01-12 15:36:27

云网络性能云性能监测应用性能监测

2019-09-26 08:33:51

Nginx技术Java

2010-03-02 10:08:28

Android源代码

2021-09-08 09:41:09

开发Go代码

2013-06-03 10:02:53

WAF绕过

2019-08-14 15:40:05

Web图片优化前端

2024-10-24 08:07:25

大语言模型LLMRAG模型

2011-04-19 10:53:05

SSAS

2011-03-02 17:56:40

DB2数据库

2011-08-19 15:48:13

SQL Server 结果集Sets使用技巧

2021-06-27 06:25:14

代码优化技巧Java

2022-09-07 00:04:37

JavaScript运算符技巧

2010-09-08 15:19:46

生产环境性能测试风险

2011-05-05 09:25:35

喷墨打印机指标术语

2024-11-15 10:45:56

2011-04-18 09:03:36

数据库查询
点赞
收藏

51CTO技术栈公众号