淺談SSAS計(jì)算中MDX性能改進(jìn)的幾個(gè)技巧
1.使用半加性度量值替代Calculation
這算是半加性度量值函數(shù)的模擬能力之一,但是,有些只在SSAS Enterprise SKU版本中才支持。但是半加性函數(shù)操作更快,差不多是MDX腳本兩本的速度
2.使用一元操作符替代Calculation
服從分配律一元操作符(與位置順序無關(guān)的操作符)一般來說要比同樣作用的賦值操作快兩倍。那是不是所有的Calculation都要抽取出來,然后用一元操作符替換呢,答案是否定的。例外就是那些不服從分配律的一元操作符(包括*,/或者數(shù)字值)。在這種情況下,才會(huì)有可能改進(jìn)性能。
3.減小集合計(jì)算的大小
當(dāng)計(jì)算值是基于集合,使用NONEMPTY函數(shù)減小集合的大小。例如,當(dāng)計(jì)算城市間每個(gè)產(chǎn)品的銷售平均值,這個(gè)查詢大約耗時(shí)1分46秒,在我的機(jī)器上:
- with member measures.x as avg(existing
- [Product].[Product].[Product].members*[Customer].[Customer Geography].[City].members,
- [Measures].[Internet Sales Amount])
- select [Date].[Calendar].[Month].members on 0,
- non empty [Product].[Subcategory].members on 1
- from [Adventure Works] where measures.x
但是添加了NONEMPTY,耗時(shí)大約39秒:
- with member measures.x as avg(existing
- nonempty( [Product].[Product].[Product].members*[Customer].[Customer Geography].[City].members,[Measures].[Internet Sales Amount])
- , [Measures].[Internet Sales Amount])
- select [Date].[Calendar].[Month].members on 0,
- non empty [Product].[Subcategory].members on 1
- from [Adventure Works] where measures.x
避免賦給cell像0, Null, “N/A”, “-“ 這樣的值
之前翻譯的文章已經(jīng)提過了,AS的引擎在排除空行方面是很高效的。下面的查詢使用"-"替換了null值,并且non empty不會(huì)排出它們:
- with member measures.x as
- iif( not isempty([Measures].[Internet Sales Amount]),[Measures].[Internet Sales Amount],"-")
- select descendants([Date].[Calendar].[Calendar Year].&[2004] ) on 0,
- non empty [Customer].[Customer Geography].[Customer].members on 1
- from [Adventure Works]
- where measures.x
Non empty操作符不能用來格式化的值上,因此要使用格式化字符串的方式來替換null值,但卻排除了空的行,耗時(shí)大約是一半的時(shí)間:
- with member measures.x as
- [Measures].[Internet Sales Amount], FORMAT_STRING = "#.00;(#.00);#.00;-"
- select descendants([Date].[Calendar].[Calendar Year].&[2004] ) on 0,
- non empty [Customer].[Customer Geography].[Customer].members on 1
- from [Adventure Works]
- where measures.x
有的讀者可能已經(jīng)觀察到,上面的兩個(gè)查詢其實(shí)是不等效的——第二個(gè)查詢完全排除了空行。
4.計(jì)算格式化值的成本
在某些情況下,采用格式化字符串的成本消耗甚至操作了求值本身。這時(shí)候就需要對比是否應(yīng)用格式化屬性時(shí)兩者的執(zhí)行時(shí)間,例如:
- select [Measures].[Internet Average Sales Amount] on 0 from [Adventure Works] cell properties value
- 如果不帶格式化的結(jié)果明顯快的話,可以使用下面的語句實(shí)現(xiàn)格式化:
- scope([Measures].[Internet Average Sales Amount]);
- FORMAT_STRING(this) = "currency";
- end scope;
執(zhí)行這個(gè)查詢(應(yīng)用了格式化),然后在決定是否采用吧
5.使用屬性
在SSAS 2000中,當(dāng)要查找一個(gè)集合中哪些成員滿足條件通常使用filter函數(shù),例如下面查找男性的顧客:
Filter(Customer.name.Name.members, Customer.name.currentmember.properties(“Gender”) = “Male”)
在SSAS 2005/2008中這樣的方式就要避免了,取而代之的是添加一個(gè)hierarchy屬性Customers.Gender :
(Customers.Gender.Male, Measures.Sales)
這個(gè)hierarchy屬性可以對終端用戶隱藏,但是對MDX表達(dá)式仍然有效。
下面查詢男性客戶的平均銷售額:
Avg(Customer.Name.Name.members*Customers.Gender.Male, Measures.Sales)
為什么使用.properties會(huì)慢呢?因?yàn)樵讷@取一個(gè)成員的時(shí)候每個(gè)成員其實(shí)都要獲取到,這樣才能得到property的值,EXISTS更快些是因?yàn)橹苯邮褂昧藘?nèi)部存儲(chǔ)結(jié)構(gòu)
6.使用IS比較操作符
當(dāng)比較成員時(shí),比較對象時(shí)使用IS,不要像下面這樣:
Iif( [Currency Code].currentmember.Name = “USA”], …)
正確的方法是:
Iif( [Currency Code].currentmember IS [Currency Code].[USA], …)
這是因?yàn)?**個(gè)語句要將成員轉(zhuǎn)型為string,這是要耗時(shí)的,而且沒有必要
7.使用NONEMPTY函數(shù)
NONEMPTY函數(shù)(SSAS 2005新增的)在提出元組方面進(jìn)行了優(yōu)化,因此下面的查詢就不可取了:
Filter(Customer.Name.members, not IsEmpty( ([Measures].[Unit Sales], [Product].[Name].[Xbox])
合適的方式是:
NonEmpty (Customer.Name.members, ([Measures].[Unit Sales], [Product].[Name].[Xbox]))
其實(shí)Filter(
另外:
- Count(Filter([year and month].[Month],
- Not IsEmpty(([Project Count], [Layer].[Layer].[web]))
- or Not IsEmpty(([Project Count], [Layer].[Layer].[server]))))
上面的方式也不可取,應(yīng)該這樣:
- Count(NonEmpty ([year and month].[Month],{([Project Count],
- [Layer].[Layer].[web]), ([Project Count], [Layer].[Layer].[server])})
8.條件計(jì)算
IIF的存在可能會(huì)破環(huán)引擎的優(yōu)化,因?yàn)閮?yōu)化器不能接受在求值過程中的表達(dá)式,這個(gè)問題可能以后會(huì)解決,但是可以采用一些方案繞過這個(gè)問題:
9.條件計(jì)算和條件域
IIF有時(shí)候用于限定應(yīng)用于集合成員的計(jì)算的域,例如:
- This =
- IIF([account].[Account Type].currentmember IS [Account].[Account Type].[Flow],
- ,
- )
上面的做法不推薦,下面可能會(huì)獲得更好的性能:
- Scope ([Account].[Account Type].[Flow]);
- This = ;
- End Scope;
- Scope ([Account].[Account Type].[Account Type].members –
- [Account].[Account Type].[Flow]);
- This = ;
- End Scope;
10.使用ValidMeasure減少計(jì)算求值的空間消耗
在SSAS 2005中,每個(gè)度量值組都有一個(gè)IgnoreUnrelatedDimensions 屬性,這個(gè)屬性定義了如何處理與度量值不相關(guān)的維度 --或者忽略這個(gè)維度,或者剔除默認(rèn)成員上的空值。
在定義計(jì)算的時(shí)候,不要使用這個(gè)behavior而是使用ValidMeasuer函數(shù),例如:
- scope(leaves([Time]), [Currency].[Currency].members - [Currency].[Currency].[Currency].[USA]
- Scope [Measures].[store Sales];
- This = iif( isempty(validmeasure([Measures].[Exchange Rate])), null, [Measures].[Store Sales]/validmeasure([Measures].[Exchange Rate]));
- End Scope;
11.使用Sum或Aggregate替代求和運(yùn)算,
避免:
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.在大數(shù)據(jù)量Crossjoin中多重的Hierarchy
如果可能,盡量使在同一緯度中的hierarchy在一起。為什么呢?因?yàn)樵谝鎯?nèi)部有一個(gè)exists在進(jìn)行Crossjoin的相鄰的集合之間。如果一個(gè)來自不用緯度的hierarchy插在兩個(gè)屬于相同緯度的hierarchy之間,exists的行為會(huì)變化,就會(huì)增加占用的空間,影響性能
13.緩存計(jì)算結(jié)果
在公式引擎內(nèi)部有一個(gè)緩存,用于重用計(jì)算結(jié)果,但是要進(jìn)行緩存,這個(gè)結(jié)果在Cube空間中就要有一個(gè)可定地址的單元格或者元組。
例如,一個(gè)應(yīng)用程序在集合中定義了一個(gè)比率:一個(gè)值除***值,并且展示為柱狀圖。
***種方法:
- with
- member measures.y as
- measures.[unit sales]
- /
- max(customers.[name].[name].members, measures.[unit sales])
- select
- measures.y on 0,
- customers.[name].[name].members on 1
- from sales
這個(gè)執(zhí)行大約需要15秒。這個(gè)計(jì)算其實(shí)效率不高,因?yàn)?**值其實(shí)是不變的,但是每次表達(dá)式都要進(jìn)行求值,下面的查詢就要好一些:
- with
- member measures.x as
- max(customers.[name].[name].members, measures.[unit sales])
- member measures.y as
- measures.[unit sales]
- /
- (measures.x,[Customers].[Customers].[All Customers] )
- select
- measures.y on 0,
- customers.[name].[name].members on 1
- from sales
這個(gè)查詢不到1秒!性能得到了數(shù)量級的提升!
不知道讀者有沒有注意到一個(gè)問題?為什么SSAS表達(dá)式中的measure.y必須要在分母中包含[Customers].[Customers].[All Customers] ,換句話說為什么不能這樣:
- with
- member measures.x as
- max(customers.[name].[name].members, measures.[unit sales])
- member measures.y as
- measures.[unit sales]
- /
- (measures.x)
- select
- measures.y on 0,
- customers.[name].[name].members on 1
- from sales
這是因?yàn)槲覀冃枰镁彺嬷怠H绻鸞Customers].[Customers].[All Customers]沒有寫上customer維度的屬性,那么單元格會(huì)跟著客戶名的引用和計(jì)算值變化,而計(jì)算值每次都要重新求值
【編輯推薦】