Wanneer u een multidimensionale database als gegevensbron in Oracle Analytics gebruikt, ervaart u mogelijk prestatieproblemen die tot suboptimaal multidimensionale expressie-query's (MDX) leiden.
Door het ontwerp te wijzigen, kunt u de MDX-query's verbeteren die door Oracle Analytics worden gegenereerd. Dit kan een enorme impact tot gevolg hebben, niet alleen op de prestaties van uw rapport maar ook op het volume aan resources dat in de database wordt gebruikt. De manier waarop u ondersteunde of niet-ondersteunde functies gebruikt, is van grote invloed op de gegenereerde MDX-query's en dus ook de prestaties.
Aangezien iedere casus uniek is, moet uw ontwikkelingsteam de opties beoordelen, de Oracle Analytics querylogbestanden analyseren en de beste oplossing voor uw casus selecteren.
In dit onderwerp worden geen prestatieproblemen beschreven die door uw infrastructuur worden veroorzaakt, zoals netwerken, browsers of presentatie van rapporten.
Methodologie
Oracle raadt aan de volgende taken uit te voeren om de prestaties te verbeteren. Het is van belang dat u de MDX-querystructuur begrijpt, alsook de querylogbestanden die Oracle Analytics genereert.
Optimalisatie selectiestappen
Wanneer u selectiestappen optimaliseert, kunt u de MDX-query's vereenvoudigen, het aantal gegenereerde MDX-query's verminderen en de prestaties verbeteren.
In de volgende afbeelding wordt een voorbeeld van een vergelijking getoond van geoptimaliseerde en niet-geoptimaliseerde selectiestappen.
.jpg
CASE-statements
De functionaliteit van het CASE
-statement wordt niet in MDX-query's ondersteund en moet altijd in Oracle Analytics worden toegepast. De logica die in deze sectie wordt uitgelegd met betrekking tot CASE
-statements is geldig voor de meeste functies die niet in MDX-query's worden ondersteund (indien null
, enzovoort).
Er zijn zowel voordelen als nadelen bij het gebruik van CASE
-statements. Wanneer u CASE
-statements in rapportformules opneemt, worden deze niet in de MDX-query opgenomen. Dit kan de MDX-query vereenvoudigen en de prestaties verbeteren. Het is echter wel zo dat u niet meer zo efficiënt kunt filteren. Dat houdt in dat de query meer records kan retourneren dan nodig is.
Hier volgen beperkingen voor het gebruik van de CASE
-statementfunctionaliteit:
CASE
-statement niet meerdere onderdelen worden gecombineerd, moet de basiskolom die in het statement wordt gebruikt, in de query worden opgenomen en de views als een afzonderlijke, verborgen kolom.CASE
-statement meerdere onderdelen worden gecombineerd, kan de basiskolom niet in de view worden opgenomen zonder dat dit het aggregatieniveau beïnvloedt. Indien dat het geval is:
SUM
, MAX
, MIN
). Dit werkt alleen als de interne aggregatieregel wordt gebruikt om onderdelen te combineren en de juiste resultaten oplevert.FILTER-functie
In tegenstelling tot de CASE
-statementfunctionaliteit kan de FILTER
-functie naar de database worden verzonden om te worden uitgevoerd.
Het belangrijkste voordeel van het gebruik van de FILTER
-functie in rapportformules is dat de selectie wordt toegepast in de MDX-query en dat het volume aan gegevens dat wordt berekend en uit de database wordt opgehaald, wordt verminderd.
Het belangrijkste nadeel van het gebruik van de FILTER
-functie is dat hiermee het aantal uitgevoerde MDX-query's kan toenemen. Standaard wordt één query uitgevoerd voor elke gebruikte FILTER
-functie.
Voorbeeld van CASE versus FILTER
In dit voorbeeld vraagt de gebruiker een rapport aan waarin de winst per kwartaal en de geselecteerde product-SKU worden getoond. Daarnaast zijn de SKU's in twaalf categorieën gegroepeerd. Aan de categorie 'Andere Cola' zijn de volgende producten van de bedrijfstak toegewezen: Cola, Diet Cola en Shared Diet Cola.
.jpg
Dit is de logische query van het CASE
-statement:
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
Er is geen groepering op basis van het CASE
-statement. Er wordt een eenvoudige MDX-query gegenereerd met het CASE
-statement dat is verwerkt door 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]
Het CASE
-statement wordt op de BI-server uitgevoerd. De instelling van de database, ingesteld op database 0:0,0
, laat dit zien:
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]
U kunt ook een filter gebruiken voor de winstmetric, zodat alleen de vereiste brancheonderdelen worden opgehaald. In dit scenario maakt u drie metrics, waarbij de bijbehorende filters zijn toegepast.
Dit is de logische query van het FILTER
-statement:
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
In dit scenario worden drie query's gegenereerd, een voor elk filter. Hierbij kunt u prestatieproblemen verwachten.
Query 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] ]]
Query 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] ]]
Query 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]
Voorbeeld waarin een productfilter is toegepast
Een betere aanpak bestaat uit het opnemen van de productkolom in het rapport met één eenheidkolom zonder filter. Maak vervolgens een filter waarin de vereiste producten zijn opgenomen. Als u de producten in verschillende categorieën wilt groeperen, gebruikt u een CASE
-statement. In dit scenario wordt er één MDX-query door de gefilterde rijen gegenereerd en ook al wordt het CASE
-statement door Oracle Analytics toegepast, de subset van gegevens wordt gebruikt en niet alle records.
Hier volgt een ander scenario, waarin CASE
-statements prestatieproblemen veroorzaken.
Een ontwikkelaar past een CASE
-statement toe om merken te hernoemen en een dashboardprompt stelt gebruikers in staat het merk te selecteren.
.jpg
.jpg
Aangezien het CASE
-statement niet in de MDX wordt ondersteund, kan het filter voor Brand2
niet in de MDX-query worden toegepast. Alle merken worden geselecteerd en dit wordt niet geoptimaliseerd.
.jpg
In dit scenario raadt Oracle u aan het CASE
-statement te verwijderen en leden in de database te hernoemen of aliassen te maken.