Oracle Analyticsでデータ・ソースとしてマルチディメンショナル・データベースを使用する場合、パフォーマンスの問題が発生し、最適ではないマルチディメンショナル式(MDX)問合せが生成される結果になることがあります。
設計を変更することで、Oracle Analyticsによって生成されるMDX問合せを改善できます。これは、レポートのパフォーマンスだけでなく、データベースで使用されるリソースの量にも非常に大きな影響を与える可能性があります。サポート対象またはサポート対象外の機能を使用する方法は、生成されるMDX問合せとパフォーマンスに大きな影響を与えます。
各ユース・ケースは一意であるため、開発チームは、オプションの確認、Oracle Analytics問合せログの分析、およびユース・ケースに応じた最適なソリューションの選択を行う必要があります。
このトピックは、ネットワーク、ブラウザ、レポート表示などのインフラストラクチャによって引き起こされるパフォーマンスの問題は対象としていません。
方法
パフォーマンスを向上させるために次のタスクを実行することをお薦めします。MDX問合せ構造と、Oracle Analyticsが生成する問合せログについて理解することが重要です。
選択ステップの最適化
選択ステップを最適化すると、MDX問合せを単純化し、生成されるMDX問合せの数を減らして、パフォーマンスを向上できます。
次の図は、最適化された選択ステップと最適化されていない選択ステップの比較の例です。
CASE文
CASE
文の機能は、MDX問合せでサポートされておらず、Oracle Analyticsで常に適用する必要があります。CASE
文に関してこの項で説明するロジックは、MDX問合せでサポートされていないほとんどの機能(if null
など)に対して有効です。
CASE
文を使用する場合、利点と欠点があります。レポート式にCASE
文を入れると、MDX問合せに含まれなくなります。これにより、MDX問合せがシンプルになり、パフォーマンスが向上します。ただし、効果的にフィルタできないというデメリットがあるため、問合せで必要以上のレコードが返される可能性があります。
CASE
文の機能を使用する場合の制限事項は次のとおりです:
CASE
文で複数のメンバーを結合しない場合、文で使用されるベース列を問合せおよび(非表示の個別列として)ビューに含める必要があります。CASE
文で複数のメンバーを結合する場合、集計のレベルに影響を与えずにベース列をビューに含めることはできません。これが該当する場合:
SUM
、MAX
、MIN
)に変更する必要があります。これが機能するのは、メンバーを結合して正しい結果を提供するために内部集計ルールが使用される場合のみです。FILTER関数
CASE
文の機能とは異なり、FILTER
関数は実行のためにデータベースに提供できます。
レポート式でFILTER
関数を使用することの主なメリットは、MDX問合せで選択が適用され、データベースから計算および取得されるデータの量が減少することです。
FILTER
関数を使用することの主なデメリットは、実行されるMDX問合せの数が増加する可能性があることです。デフォルトでは、使用されるFILTER
関数ごとに1つの問合せが実行されます。
CASEとFILTERの例
この例では、ユーザーが、四半期および選択した製品SKU別の利益を示すレポートをリクエストします。また、SKUは12のカテゴリにグループ化されています。Other Colaカテゴリには、Cola、Diet ColaおよびShared Diet ColaというLOBの製品が割り当てられています。
CASE
文の論理問合せを次に示します:
SELECT 0 s_0, CASE when XSA('Admin'.'Sample.BasicPM')."Product"."Product SKU" in ('Cola','Diet Cola','Shared Diet Cola') THEN 'Other Cola' ELSE XSA('Admin'.'Sample.BasicPM')."Product"."Product SKU" END s_1, DESCRIPTOR_IDOF(XSA('Admin'.'Sample.BasicPM')."Product"."Category") s_2, DESCRIPTOR_IDOF(XSA('Admin'.'Sample.BasicPM')."Product"."Product SKU") s_3, DESCRIPTOR_IDOF(XSA('Admin'.'Sample.BasicPM')."Year"."Quarter") s_4, SORTKEY(XSA('Admin'.'Sample.BasicPM')."Product"."Category") s_5, SORTKEY(XSA('Admin'.'Sample.BasicPM')."Product"."Product SKU") s_6, SORTKEY(XSA('Admin'.'Sample.BasicPM')."Year"."Quarter") s_7, XSA('Admin'.'Sample.BasicPM')."Product"."Category" s_8, XSA('Admin'.'Sample.BasicPM')."Product"."Product SKU" s_9, XSA('Admin'.'Sample.BasicPM')."Year"."Quarter" s_10, XSA('Admin'.'Sample.BasicPM')."Basic"."Profit" s_11 FROM XSA('Admin'.'Sample.BasicPM') ORDER BY 8 ASC NULLS LAST, 11 ASC NULLS LAST, 5 ASC NULLS LAST, 2 ASC NULLS LAST, 7 ASC NULLS LAST, 10 ASC NULLS LAST, 4 ASC NULLS LAST, 6 ASC NULLS LAST, 9 ASC NULLS LAST, 3 ASC NULLS LAST FETCH FIRST 125001 ROWS ONLY
CASE
文に基づくグループ化はありません。Oracle Analyticsによって処理されるCASE
文を使用した単純なMDX問合せが生成されます。
With set [_Product3] as 'Descendants([Product], [Product].Generations(3), leaves)' set [_Year2] as 'Descendants([Year], [Year].Generations(2), leaves)' select { [Measures].[Profit] } on columns, NON EMPTY {crossjoin({[_Year2]},{[_Product3]})} properties GEN_NUMBER, [Product].[MEMBER_UNIQUE_NAME], [Product].[Memnor], [Year].[MEMBER_UNIQUE_NAME], [Year].[Memnor] on rows from [Sample.Basic]
CASE
文は、BIサーバーで実行されます。これはdatabase 0:0,0
に設定されたデータベース設定からわかります:
RqList <<11777451>> [for database 0:0,0] D1.c6 as c6 [for database 0:0,0], D1.c4 as c4 [for database 0:0,0], case when D1.c7 in ([ 'Cola', 'Diet Cola', 'Shared Diet Cola'] ) then 'Other Cola' else D1.c7 end as c2 [for database 0:0,0], D1.c5 as c5 [for database 0:0,0], D1.c3 as c3 [for database 0:0,0], D1.c1 as c1 [for database 0:0,0], D1.c7 as c7 [for database 0:0,0], D1.c8 as c8 [for database 0:0,0]
別の方法として、利益メトリックに対してフィルタを使用し、必要なLOBメンバーのみを取得できます。このシナリオでは、対応するフィルタが適用された3つのメトリックを作成します。
FILTER
文の論理問合せを次に示します:
SELECT 0 s_0, DESCRIPTOR_IDOF(XSA('Admin'.'Sample.BasicPM')."Product"."Category") s_1, DESCRIPTOR_IDOF(XSA('Admin'.'Sample.BasicPM')."Product"."Product SKU") s_2, DESCRIPTOR_IDOF(XSA('Admin'.'Sample.BasicPM')."Year"."Quarter") s_3, SORTKEY(XSA('Admin'.'Sample.BasicPM')."Product"."Category") s_4, SORTKEY(XSA('Admin'.'Sample.BasicPM')."Product"."Product SKU") s_5, SORTKEY(XSA('Admin'.'Sample.BasicPM')."Year"."Quarter") s_6, XSA('Admin'.'Sample.BasicPM')."Product"."Category" s_7, XSA('Admin'.'Sample.BasicPM')."Product"."Product SKU" s_8, XSA('Admin'.'Sample.BasicPM')."Year"."Quarter" s_9, FILTER(XSA('Admin'.'Sample.BasicPM')."Basic"."Profit" USING XSA('Admin'.'Sample.BasicPM')."Product"."Product SKU" in ('Cola','Diet Cola','Shared Diet Cola')) s_10, FILTER(XSA('Admin'.'Sample.BasicPM')."Basic"."Profit" USING XSA('Admin'.'Sample.BasicPM')."Product"."Product SKU" in ('Sasprilla','Birch Beer','Dark Cream')) s_11, FILTER(XSA('Admin'.'Sample.BasicPM')."Basic"."Profit" USING XSA('Admin'.'Sample.BasicPM')."Product"."Product SKU" in ('xxxxx')) s_12 FROM XSA('Admin'.'Sample.BasicPM') ORDER BY 7 ASC NULLS LAST, 10 ASC NULLS LAST, 4 ASC NULLS LAST, 6 ASC NULLS LAST, 9 ASC NULLS LAST, 3 ASC NULLS LAST, 5 ASC NULLS LAST, 8 ASC NULLS LAST, 2 ASC NULLS LAST FETCH FIRST 125001 ROWS ONLY
このシナリオでは、フィルタごとに1つずつ、3つの問合せが生成され、パフォーマンスの問題が発生します。
問合せ1:
With set [_Product3] as 'Filter([Product].Generations(3).members, ((IIF(IsValid([Product].CurrentMember.MEMBER_ALIAS), [Product].CurrentMember.MEMBER_ALIAS, [Product].CurrentMember.MEMBER_Name) = "xxxxx")))' set [_Year2] as 'Descendants([Year], [Year].Generations(2), leaves)' select { [Measures].[Profit] } on columns, NON EMPTY {crossjoin({[_Year2]},{[_Product3]})} properties MEMBER_NAME, GEN_NUMBER, property_expr([Product], [MEMBER_NAME], Ancestor(currentaxismember(), [Product].Generations(2)), "Category_Null_Alias_Replacement"), property_expr([Product], [Default], Ancestor(currentaxismember(), [Product].Generations(2)), "Category"), property_expr([Product], [MEMBER_UNIQUE_NAME], Ancestor(currentaxismember(), [Product].Generations(2)), "Category - Member Key"), property_expr([Product], [Memnor], Ancestor(currentaxismember(), [Product].Generations(2)), "Category - Memnor"), [Product].[MEMBER_UNIQUE_NAME], [Product].[Memnor], [Year].[MEMBER_UNIQUE_NAME], [Year].[Memnor] on rows from [Sample.Basic] ]]
問合せ2:
With set [_Product3] as 'Filter([Product].Generations(3).members, ((IIF(IsValid([Product].CurrentMember.MEMBER_ALIAS), [Product].CurrentMember.MEMBER_ALIAS, [Product].CurrentMember.MEMBER_Name) = "Birch Beer") OR (IIF(IsValid([Product].CurrentMember.MEMBER_ALIAS), [Product].CurrentMember.MEMBER_ALIAS, [Product].CurrentMember.MEMBER_Name) = "Dark Cream") OR (IIF(IsValid([Product].CurrentMember.MEMBER_ALIAS), [Product].CurrentMember.MEMBER_ALIAS, [Product].CurrentMember.MEMBER_Name) = "Sasprilla")))' set [_Year2] as 'Descendants([Year], [Year].Generations(2), leaves)' select { [Measures].[Profit] } on columns, NON EMPTY {crossjoin({[_Year2]},{[_Product3]})} properties MEMBER_NAME, GEN_NUMBER, property_expr([Product], [MEMBER_NAME], Ancestor(currentaxismember(), [Product].Generations(2)), "Category_Null_Alias_Replacement"), property_expr([Product], [Default], Ancestor(currentaxismember(), [Product].Generations(2)), "Category"), property_expr([Product], [MEMBER_UNIQUE_NAME], Ancestor(currentaxismember(), [Product].Generations(2)), "Category - Member Key"), property_expr([Product], [Memnor], Ancestor(currentaxismember(), [Product].Generations(2)), "Category - Memnor"), [Product].[MEMBER_UNIQUE_NAME], [Product].[Memnor], [Year].[MEMBER_UNIQUE_NAME], [Year].[Memnor] on rows from [Sample.Basic] ]]
問合せ3:
With set [_Product3] as 'Filter([Product].Generations(3).members, ((IIF(IsValid([Product].CurrentMember.MEMBER_ALIAS), [Product].CurrentMember.MEMBER_ALIAS, [Product].CurrentMember.MEMBER_Name) = "Cola") OR (IIF(IsValid([Product].CurrentMember.MEMBER_ALIAS), [Product].CurrentMember.MEMBER_ALIAS, [Product].CurrentMember.MEMBER_Name) = "Diet Cola") OR (IIF(IsValid([Product].CurrentMember.MEMBER_ALIAS), [Product].CurrentMember.MEMBER_ALIAS, [Product].CurrentMember.MEMBER_Name) = "Shared Diet Cola")))' set [_Year2] as 'Descendants([Year], [Year].Generations(2), leaves)' select { [Measures].[Profit] } on columns, NON EMPTY {crossjoin({[_Year2]},{[_Product3]})} properties MEMBER_NAME, GEN_NUMBER, property_expr([Product], [MEMBER_NAME], Ancestor(currentaxismember(), [Product].Generations(2)), "Category_Null_Alias_Replacement"), property_expr([Product], [Default], Ancestor(currentaxismember(), [Product].Generations(2)), "Category"), property_expr([Product], [MEMBER_UNIQUE_NAME], Ancestor(currentaxismember(), [Product].Generations(2)), "Category - Member Key"), property_expr([Product], [Memnor], Ancestor(currentaxismember(), [Product].Generations(2)), "Category - Memnor"), [Product].[MEMBER_UNIQUE_NAME], [Product].[Memnor], [Year].[MEMBER_UNIQUE_NAME], [Year].[Memnor] on rows from [Sample.Basic]
製品フィルタ適用例
より適切なアプローチとしては、フィルタなしで1つのメジャー列を使用して、レポートに製品列を含めます。次に、必要な製品を含むフィルタを作成します。製品を別のカテゴリにグループ化する場合は、CASE
文を使用します。このシナリオでは、フィルタされた行を使用して単一のMDX問合せが生成され、CASE
文がOracle Analyticsによって適用されても、すべてのレコードではなくデータのサブセットが使用されます。
CASE
文によってパフォーマンスの問題が引き起こされる別のシナリオを示します。
開発者がブランド名を変更するためのCASE
文を適用し、ユーザーはダッシュボード・プロンプトを使用してブランドを選択できます
CASE
文はMDXでサポートされていないため、Brand2
に対するフィルタはMDX問合せで適用できません。すべてのブランドが選択され、最適化されません。
このタイプのシナリオでは、CASE
文を削除して、データベースのメンバー名を変更するか、別名を作成することをお薦めします。