Count(Filter(...)) の最適化 - その2:AS2008 での実験結果
前回の投稿「Count(Filter(...)) の最適化(Mosha Pasumansky さんのブログから)」http://blogs.sqlpassj.org/nagasaki/archive/2008/08/31/25540.aspx の続きです。
AS2008 で、サンプルデータベース「Adventure Works DW 2008」のサンプルキューブ「Adventure Works.cube」を使用して試してみました。パフォーマンス測定のため「MDX Studio」を使用させてもらっています。
(1)
WITH MEMBER [Measures].[High Volume Products Count] AS Count(Filter([Product].[Product].[Product], [Measures].[Internet Order Quantity] > 5)) SELECT [Customer].[Customer Geography].[Country] ON 0 ,[Date].[Calendar].[Date].MEMBERS ON 1 FROM [Adventure Works] WHERE [Measures].[High Volume Products Count] Time : 40 sec 203 ms Calc covers : 0 Cells calculated : 2836944 Sonar subcubes : 2 SE queries : 1 Cache hits : 3 Cache misses : 0 Cache inserts : 0 Cache lookups : 3 Memory Usage KB : 52
AS2005(前回投稿で、同じクエリで約30秒) より遅くなったということではありません(実行環境が違います)。これが基本速度です。
(2)
WITH MEMBER [Measures].[High Volume Products Count] AS Count(Filter(Exists([Product].[Product].[Product], , "Internet Sales"), [Measures].[Internet Order Quantity] > 5)) SELECT [Customer].[Customer Geography].[Country] ON 0 ,[Date].[Calendar].[Date].MEMBERS ON 1 FROM [Adventure Works] WHERE [Measures].[High Volume Products Count] Time : 12 sec 312 ms Calc covers : 0 Cells calculated : 50217 Sonar subcubes : 7277 SE queries : 7276 Cache hits : 7278 Cache misses : 0 Cache inserts : 0 Cache lookups : 7278 Memory Usage KB : 1212
前回投稿の AS2005 と同じような振る舞いです。
(3)
WITH MEMBER [Measures].[High Volume Products Count] AS Sum([Product].[Product].[Product], Iif([Measures].[Internet Order Quantity] > 5, 1, 0)) SELECT [Customer].[Customer Geography].[Country] ON 0 ,[Date].[Calendar].[Date].MEMBERS ON 1 FROM [Adventure Works] WHERE [Measures].[High Volume Products Count] Time : 8 sec 156 ms Calc covers : 0 Cells calculated : 7128 Sonar subcubes : 1 SE queries : 1 Cache hits : 3 Cache misses : 2 Cache inserts : 1 Cache lookups : 5 Memory Usage KB : 0
前回投稿の AS2005 と同じような振る舞いです。
(4)
WITH MEMBER [Measures].[High Volume Products Count] AS Sum([Product].[Product].[Product], Iif([Measures].[Internet Order Quantity] > 5, 1, NULL)) SELECT [Customer].[Customer Geography].[Country] ON 0 ,[Date].[Calendar].[Date].MEMBERS ON 1 FROM [Adventure Works] WHERE [Measures].[High Volume Products Count] Time : 578 ms Calc covers : 0 Cells calculated : 7128 Sonar subcubes : 1 SE queries : 1 Cache hits : 4 Cache misses : 0 Cache inserts : 0 Cache lookups : 4 Memory Usage KB : 0
前回投稿の AS2005 より高速です。Mosha さんが述べているように、2008 では NON_EMPTY_BEHAVIOR の明示が無くても高速化されます。
(5)
--MDX Script CREATE HIDDEN Summator; [Measures].[Summator] = Iif([Measures].[Internet Order Quantity] > 5, 1, NULL); NON_EMPTY_BEHAVIOR([Measures].[Summator]) = [Measures].[Internet Order Quantity]; --Query WITH MEMBER [Measures].[High Volume Products Count] AS Sum([Product].[Product].[Product], [Measures].[Summator]) SELECT [Customer].[Customer Geography].[Country] ON 0 ,[Date].[Calendar].[Date].MEMBERS ON 1 FROM [Adventure Works] WHERE [Measures].[High Volume Products Count]
非推奨になっていますが一応 NON_EMPTY_BEHAVIOR を試してみました。1 分以内に結果が戻らず、計測不能という結果です。 AS2008 での NON_EMPTY_BEHAVIOR の使用は避けたほうがよさそうです。
(6)
ここまでの最速は(4)ですが、(4)の結果セットには 0 ではなく NULL が表示されており、厳密に言えば(1)〜(3)までの結果セットとは異なっています。そこで、結果セットに NULL ではなく 0 を表示するように以下のような記述に変更してみました。
WITH MEMBER [Measures].[High Volume Products Count] AS Iif(Sum([Product].[Product].[Product], Iif([Measures].[Internet Order Quantity] > 5, 1, NULL)) = NULL, 0, Sum([Product].[Product].[Product], Iif([Measures].[Internet Order Quantity] > 5, 1, NULL))) SELECT [Customer].[Customer Geography].[Country] ON 0 ,[Date].[Calendar].[Date].MEMBERS ON 1 FROM [Adventure Works] WHERE [Measures].[High Volume Products Count] Time : 1 sec 968 ms Calc covers : 0 Cells calculated : 78588 Sonar subcubes : 1 SE queries : 3 Cache hits : 8 Cache misses : 0 Cache inserts : 0 Cache lookups : 8 Memory Usage KB : 0
この書き方ですと、実行時間は 2 秒程度で済んでいますが計算量("Cells calculated")がかなり増えてしまっています。
(7)
そこで、別の計算メンバを使用して、結果セットに NULL ではなく 0 を表示するような別の方法に変更してみました。
WITH MEMBER [Measures].[High Volume Products Count] AS Sum([Product].[Product].[Product], Iif([Measures].[Internet Order Quantity] > 5, 1, NULL)), SOLVE_ORDER = 1 MEMBER [Measures].[High Volume Products Count 2] AS Iif([Measures].[High Volume Products Count] = NULL, 0, [Measures].[High Volume Products Count]), SOLVE_ORDER = 2 SELECT [Customer].[Customer Geography].[Country] ON 0 ,[Date].[Calendar].[Date].MEMBERS ON 1 FROM [Adventure Works] WHERE [Measures].[High Volume Products Count 2] Time : 812 ms Calc covers : 0 Cells calculated : 7128 Sonar subcubes : 1 SE queries : 1 Cache hits : 6 Cache misses : 0 Cache inserts : 0 Cache lookups : 6 Memory Usage KB : 0
この書き方であれば、約 0.6 秒の(4) と比較しても遜色ありません。"Cells calculated" の値も (4) と同じです。
まとめ
・AS2008 でも Count(Filter(...)) の書き方は自動的には(インスタンスのクエリオプティマイザでは)最適化されない。 Sum + Iif( , , NULL) に変換する必要がある。
・AS2008 では NON_EMPTY_BEHAVIOR を明示的に指定する必要はない。(逆に、指定したら非常に遅くなる振る舞いを見せた。)
・表示上、NULL から 0 に変更しておきたい場合は Sum + Iif 部分 が 1 回の計算で済むような工夫が望ましい。具体例は (7) を参照のこと。