다차원 데이터베이스 질의의 성능 조정

Oracle Analytics가 다차원 데이터베이스를 데이터 소스로 사용하는 경우 성능에 큰 영향을 끼칠 수 있는 추가 설계 고려사항이 있습니다.

성능 향상 설계 솔루션은 사용 사례에 따라 다르다는 점을 이해해야 합니다. 이 항목에서는 항상 적용해야 하는 범용 솔루션이나 최적의 방법을 제공하지 않습니다. 대신 분석 및 생성된 코드의 성능을 향상시키는 데 도움이 되는 튜닝 방법과 기술을 제공합니다.

옵션을 검토하고, Oracle Analytics 질의 로그를 분석하고, 사용 사례에 맞는 최적의 솔루션을 선택하는 것은 개발 팀의 책임입니다.

이 항목에서는 네트워크, 브라우저 또는 보고서 표시와 같이 인프라로 인해 발생하는 성능 문제를 다루지 않습니다.

방법론

성능을 향상시키려면 다음 태스크를 완료하는 것이 좋습니다. 이러한 태스크의 필요 조건으로 MDX(다차원 표현식) 질의 구조와 Oracle Analytics가 생성하는 질의 로그를 이해해야 합니다. 기본 태스크는 다음과 같습니다.

  • 생성되는 MDX를 단순화합니다.
  • 생성되는 MDX 질의 수를 줄입니다.
  • MDX에서 최적의 필터와 선택 항목이 적용되었는지 확인합니다.
  • 다차원 데이터베이스 측에서는 DBA(데이터베이스 관리자)와 함께 성능을 조정하고 소스 데이터베이스가 여전히 제대로 작동하지 않는 이유를 확인합니다.
  • DBA 피드백을 기반으로 분석을 수정합니다.

선택 단계 최적화

선택 단계를 최적화하면 MDX 질의를 단순화하고 생성되는 MDX 질의 수를 줄여 성능을 향상시킬 수 있습니다.

예제는 다음과 같습니다.

GUID-43E6F348-B14C-40DC-8C21-DA34DAE44344-default.jpg에 대한 설명이 이어집니다.
.jpg''

CASE

CASE 문 기능은 MDX에서 지원되지 않으며 항상 Oracle Analytics에서 적용되어야 합니다. CASE 문과 관련하여 아래 설명된 논리는 MDX에서 지원되지 않는 대부분의 함수(if null 등)에 대해 적합합니다.

CASE 문을 사용할 때 장점과 단점이 있습니다. 보고서 공식에 CASE 문을 포함하면 MDX에 포함되지 않습니다. 이렇게 하면 MDX 질의가 간소화되고 성능이 향상될 수 있습니다. 그러나 반대급부로 효과적으로 필터링할 수 없으며, 이는 필요한 것보다 많은 레코드가 질의에 반환될 수 있음을 의미합니다.

알 수 있듯이 각 사용 사례는 고유합니다. 핵심 목표는 MDX 질의를 단순화하는 동시에 최적의 필터와 선택 항목을 적용하는 것입니다.

CASE 문 기능을 사용할 때 제한사항은 다음과 같습니다.

  • CASE 문이 여러 멤버를 결합하지 않는 경우 문에 사용된 기본 열은 질의와 뷰에 숨겨진 별도 열로 포함되어야 합니다.
  • CASE 문이 여러 멤버를 결합하는 경우 집계 레벨에 영향을 주지 않으면서 기본 열을 뷰에 포함할 수 없습니다. 다음과 같습니다.
    • 측정항목의 집계 규칙이 외부 집계가 아닌 경우 기본 열은 질의에서 제외되어야 합니다.
    • 측정항목의 집계 규칙이 외부 집계인 경우 기본 열은 질의에 포함되고 뷰에서 제외되어야 합니다. 측정항목의 집계 규칙을 기본값에서 단순 내부 집계 규칙(SUM, MAX, MIN)으로 변경해야 합니다. 이는 내부 집계 규칙을 사용하여 멤버를 결합하고 올바른 결과를 제공하는 경우에만 작동합니다.

FILTER 함수

CASE 문과 달리 FILTER 함수는 실행을 위해 데이터베이스로 전달될 수 있습니다.

  • 보고서 공식에서 FILTER 함수를 사용할 때의 주요 이점은 선택 항목이 MDX 질의에 적용되고 데이터베이스에서 계산 및 검색되는 데이터의 양이 줄어든다는 것입니다.
  • FILTER 함수 사용의 주요 단점은 실행되는 MDX 질의 수가 늘어날 수 있다는 것입니다. 기본적으로 사용된 각 FILTER 함수에 대해 하나의 질의가 실행됩니다.

앞서 언급한 대로 각 사용 사례는 고유합니다. 목표는 MDX 질의를 단순화하는 동시에 최적의 필터와 선택 항목을 적용하는 것입니다.

CASEFILTER 시나리오

동일한 시나리오로 CASE 기능과 FILTER 기능을 사용하는 결과를 살펴보겠습니다.

사용자는 분기별 수익 및 선택된 제품 SKU를 보여주는 보고서를 요청합니다. 또한 SKU는 12개 범주로 그룹화됩니다. "Other Cola" 범주에는 LOB의 제품(Cola, Diet Cola, Shared Diet Cola 등)이 지정되어 있습니다.

GUID-7198F143-54E6-4A48-9579-96624936A94D-default.jpg에 대한 설명이 이어집니다.
.jpg''

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가 생성됩니다. 생성되는 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 Server에서 실행되며 이는 "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]

Filter 문 시나리오

수익 측정항목에 대해 필터를 사용하여 필요한 LOB 멤버만 검색할 수도 있습니다. 이 시나리오에서는 해당 필터가 적용된 세 개의 측정항목을 생성합니다.

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:

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]

제품 필터 적용 시나리오

더 나은 접근 방식은 필터 없이 단일 측정항목 열이 있는 보고서에 제품 열을 포함하는 것입니다. 그런 다음 필요한 제품을 포함하는 필터를 생성합니다. 제품을 다른 범주로 그룹화하려면 CASE 문을 사용할 수 있습니다. 이 시나리오에서는 필터링된 행으로 생성되는 단일 MDX 질의가 있으며 CASE 문이 Oracle Analytics에 의해 적용되더라도 모든 레코드가 아닌 데이터 부분 집합이 사용됩니다.

CASE 문이 성능 문제를 일으키는 다른 시나리오를 살펴보겠습니다.

개발자는 CASE 문을 적용하여 브랜드 이름을 바꾸며, 대시보드 프롬프트를 통해 사용자는 브랜드를 선택할 수 있습니다.

GUID-56356AA9-2AF6-4A67-8ADD-FC4F7F70306C-default.jpg에 대한 설명이 이어집니다.
.jpg''

GUID-E63719C8-9936-412B-8228-F20E8F048C46-default.jpg에 대한 설명이 이어집니다.
.jpg''

MDX에서는 CASE 문이 지원되지 않으므로 'Brand2'에 대한 필터는 MDX 질의에서 적용될 수 없습니다. 모든 브랜드가 선택되었으며 최적화되지 않았습니다.

GUID-6BE1F274-8257-4E31-8D42-406357A07B2A-default.jpg에 대한 설명이 이어집니다.
.jpg''

이 시나리오에서는 CASE 문을 제거하고 데이터베이스의 멤버 이름을 바꾸거나 별칭을 생성하는 것이 좋습니다.

다차원 데이터베이스를 데이터 소스로 사용하는 경우 Oracle Analytics에서 성능 문제가 발생하여 최적이 아닌 MDX 생성 질의가 생성될 수 있습니다. 설계를 수정하면 Oracle Analytics가 생성하는 MDX 질의를 향상시킬 수 있습니다. 이는 보고서 성능뿐만 아니라 데이터베이스에서 사용되는 리소스의 양에도 큰 영향을 줄 수 있습니다. MDX에서 지원되거나 지원되지 않는 함수를 활용하는 방법에 주의해야 합니다. 이는 생성되는 MDX 질의와 성능에 큰 영향을 끼치기 때문입니다.