Atunci când utilizaţi o bază de date multidimensională ca sursă de date în Oracle Analytics, puteţi întâmpina probleme de performanţă, în urma cărora sunt generate interogări de expresie multidimensională (MDX) suboptime.
Prin modificarea designului, puteţi îmbunătăţi interogările MDX pe care le generează Oracle Analytics. Acest lucru poate avea un impact semnificativ nu doar asupra performanţei rapoartelor dvs., ci şi asupra volumului de resurse utilizate în baza de date. Modul în care utilizaţi funcţiile acceptate sau neacceptate are un impact semnificativ asupra interogărilor MDX generate şi, prin urmare, asupra performanţei.
Având în vedere că fiecare caz de utilizare este unic, echipa de dezvoltare ar trebui să examineze opţiunile, să analizeze jurnalele de interogări Oracle Analytics şi să selecteze cea mai bună soluţie pentru cazul dvs. de utilizare.
Acest subiect nu se referă la problemele de performanţă cauzate de infrastructura dvs., cum ar fi problemele de reţea, browser sau prezentare a raportului.
Metodologie
Oracle recomandă să efectuaţi următoarele sarcini pentru a îmbunătăţi performanţa. Este important să înţelegeţi că structura de interogări MDX, precum şi jurnalele de interogări pe care le generează Oracle Analytics.
Optimizarea paşilor de selecţie
Atunci când optimizaţi paşii de selecţie, puteţi simplifica interogările MDX, reduce numărul de interogări MDX generate şi îmbunătăţi performanţa.
Următoarea figură prezintă un exemplu de comparaţie a paşilor de selecţie optimizaţi şi neoptimizaţi.

.jpg
Instrucţiuni CASE
Funcţionalitatea instrucţiunii CASE nu este acceptată în interogările MDX şi trebuie aplicată întotdeauna în Oracle Analytics. Logica explicată în această secţiune cu privire la instrucţiunile CASE este valabilă pentru majoritatea funcţiilor care nu sunt acceptate în interogările MDX (if null etc.).
Există avantaje şi dezavantaje ale utilizării instrucţiunilor CASE. Atunci când includeţi instrucţiuni CASE în formulele raportului, acestea nu sunt incluse în interogarea MDX. Acest lucru poate simplifica interogarea MDX şi poate îmbunătăţi performanţa. Cu toate acestea, dezavantajul este că nu puteţi filtra la fel de eficient, ceea ce înseamnă că interogarea ar putea returna mai multe înregistrări decât este necesar.
Următoarele sunt restricţii pentru utilizarea funcţionalităţii instrucţiunii CASE:
CASE nu combină mai mulţi membri, coloana de bază utilizată în instrucţiune ar trebui inclusă în interogare şi vizualizări ca o coloană ascunsă separată.CASE combină mai mulţi membri, coloana de bază nu poate fi inclusă în vizualizare fără a afecta nivelul de agregare. În acest caz:
SUM, MAX, MIN). Acest lucru este posibil numai dacă regula de agregare internă este utilizată pentru a combina membri şi produce rezultate corecte.Funcţia FILTER
Spre deosebire de funcţionalitatea instrucţiunii CASE, funcţia FILTER poate fi expediată către baza de date pentru execuţie.
Principalul avantaj al utilizării funcţiei FILTER în formulele raportului este că selecţia se aplică în interogarea MDX şi volumul de date calculate şi preluate din baza de date este redus.
Principalul dezavantaj al utilizării funcţiei FILTER este că poate mări numărul de interogări MDX executate. În mod prestabilit, se execută o singură interogare pentru fiecare funcţie FILTER utilizată.
Exemplu CASE vs. FILTER
În acest exemplu, un utilizator solicită un raport care prezintă profitul în funcţie de semestru şi codul unic de stocare al produsului selectat. În plus, codurile unice de stocare sunt grupate împreună în 12 categorii. Categoria "Alte tipuri de Cola" are următoarele produse din domeniul de activitate asignate: Cola, Diet Cola şi Shared Diet Cola.

.jpg
Iată interogarea logică pentru instrucţiunea 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 ONLYNu există grupare pe baza instrucţiunii CASE. Este generată o interogare MDX simplă, cu instrucţiunea CASE procesată de Oracle Analytics:
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]Instrucţiunea CASE este executată pe serverul BI şi este văzută de setarea bazei de date setată la 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]Alternativ, puteţi utiliza un filtru pentru subindicatorul de profit, pentru a prelua doar membrii necesari din domeniul de activitate. În acest scenariu, creaţi trei metrici cu filtrele corespunzătoare aplicate.
Iată interogarea logică pentru instrucţiunea 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În acest scenariu, sunt generate trei interogări, câte una pentru fiecare filtru, şi puteţi întâmpina probleme de performanţă.
Interogarea 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]
]]Interogarea 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]
]]Interogarea 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]Exemplu de filtru aplicat pentru produs
O abordare mai eficientă presupune includerea în raport a coloanei de produse, cu o singură coloană de subindicatori, fără filtru. Apoi, creaţi un filtru care include produsele necesare. Dacă doriţi să grupaţi produsele în categorii diferite, utilizaţi o instrucţiune CASE. În acest scenariu, se generează o singură interogare MDX cu rândurile filtrate şi chiar dacă este aplicată instrucţiunea CASE de către Oracle Analytics, utilizează subsetul de date, nu toate înregistrările.
Iată un alt scenariu, în care instrucţiunea CASE provoacă probleme de performanţă.
Un dezvoltator aplică o instrucţiune CASE pentru a redenumi mărcile, iar un prompt de dashboard permite utilizatorilor să selecteze marca.

.jpg

.jpg
Deoarece instrucţiunea CASE nu este acceptată în MDX, filtrul pentru Brand2 nu poate fi aplicat în interogarea MDX. Toate mărcile sunt selectate, fără optimizare.

.jpg
În acest tip de scenariu, Oracle vă recomandă să eliminaţi instrucţiunea CASE şi să redenumiţi membrii din baza de date sau să creaţi aliasuri.