Wenn Sie in Oracle Analytics eine multidimensionale Datenbank als Datenquelle verwenden, treten unter Umständen Performanceprobleme auf, aufgrund derer suboptimale Abfragen mit multidimensionalen Ausdrücken (MDX-Abfragen) generiert werden.
Indem Sie das Design ändern, können Sie die von Oracle Analytics generierten MDX-Abfragen verbessern. Das kann enorme Auswirkungen auf die Berichtsperformance und die Menge der belegten Ressourcen in der Datenbank haben. Wie Sie unterstützte oder nicht unterstützte Funktionen verwenden, hat enorme Auswirkungen auf die generierten MDX-Abfragen und damit die Performance.
Weil jeder Anwendungsfall einzigartig ist, muss das Entwicklungsteam die Optionen prüfen, die Oracle Analytics-Abfragelogs analysieren und die beste Lösung für Ihren Anwendungsfall auswählen.
In diesem Thema werden keine Performanceprobleme behandelt, die aufgrund Ihrer Infrastruktur auftreten, wie Netzwerke, Browser oder Berichtspräsentation.
Methode
Oracle empfiehlt, dass Sie die folgenden Maßnahmen ergreifen, um die Performance zu verbessern. Es ist wichtig, dass Sie sowohl die MDX-Abfragestruktur als auch die Abfragelogs verstehen, die Oracle Analytics generiert.
Optimierung der Auswahlschritte
Wenn Sie Auswahlschritte optimieren, können Sie die MDX-Abfragen vereinfachen, die Anzahl der generierten MDX-Abfragen reduzieren und die Performance steigern.
Die folgende Abbildung zeigt ein Beispiel für einen Vergleich zwischen optimierten und nicht optimierten Auswahlschritten.
.jpg
CASE-Anweisungen
CASE
-Anweisungsfunktionalität wird in MDX-Abfragen nicht unterstützt und muss immer in Oracle Analytics angewendet werden. Die in diesem Abschnitt erläuterte Logik in Bezug auf CASE
-Anweisungen gilt für die meisten Funktionen, die in MDX-Abfragen nicht unterstützt werden (if null
usw.).
Die Verwendung von CASE
-Anweisungen hat Vor- und Nachteile. Wenn Sie CASE
-Anweisungen in Berichtsformeln einschließen, werden sie nicht in die MDX-Abfrage aufgenommen. Dadurch können Sie die MDX-Abfrage vereinfachen und die Performance verbessern. Das bringt allerdings den Nachteil mit sich, dass die Filterung weniger effektiv wird. Die Abfrage könnte also mehr Datensätze als notwendig zurückgeben.
Bei der Verwendung der CASE
-Anweisungsfunktionalität gelten die folgenden Einschränkungen:
CASE
-Anweisung nicht mehrere Elemente kombiniert, muss die in der Anweisung verwendete Basisspalte als ausgeblendete separate Spalte in die Abfrage und die Ansichten aufgenommen werden.CASE
-Anweisung mehrere Elemente kombiniert, kann die Basisspalte nicht ohne Auswirkungen auf die Aggregationsebene in die Ansicht aufgenommen werden. In diesem Fall gilt Folgendes:
SUM
, MAX
, MIN
). Das funktioniert nur, wenn die interne Aggregationsregel zum Kombinieren von Elementen verwendet wird und korrekte Ergebnisse liefert.FILTER-Funktion
Im Gegensatz zur CASE
-Anweisungsfunktion kann die FILTER
-Funktion zur Ausführung an die Datenbank übergeben werden.
Der Hauptvorteil der FILTER
-Funktion in Berichtsformeln besteht darin, dass die Auswahl in der MDX-Abfrage angewendet wird und die Menge der Daten, die berechnet und von der Datenbank abgerufen werden, reduziert wird.
Der Hauptnachteil der FILTER
-Funktion besteht darin, dass sich die Anzahl der ausgeführten MDX-Abfragen dadurch erhöhen könnte. Standardmäßig wird eine Abfrage für jede verwendete FILTER
-Funktion ausgeführt.
Beispiel zum Vergleich von CASE und FILTER
In diesem Beispiel fordert ein Benutzer einen Bericht an, der den Gewinn nach Quartal und ausgewählter Produkt-SKU anzeigt. Darüber hinaus sind die SKUs in 12 Kategorien eingeteilt. Der Kategorie "Other Cola" sind die folgenden Geschäftsbereichsprodukte zugewiesen: Cola, Diet Cola und Shared Diet Cola.
.jpg
Logische Abfrage der CASE
-Anweisung:
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
Es gibt keine Gruppierung auf der Basis der CASE
-Anweisung. Es wird eine einfache MDX-Abfrage erstellt, und die CASE
-Anweisung wird von Oracle Analytics verarbeitet:
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]
Die CASE
-Anweisung wird auf dem BI-Server ausgeführt, wie an der Datenbankeinstellung database 0:0,0
zu erkennen ist:
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 dazu können Sie einen Filter für die Gewinnmetrik verwenden, um nur die erforderlichen Geschäftsbereichselemente abzurufen. In diesem Szenario erstellen Sie drei Metriken mit den entsprechenden angewendeten Filtern.
Logische Abfrage der FILTER
-Anweisung:
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 diesem Szenario werden drei Abfragen generiert, eine für jeden Filter, und es treten Performanceprobleme auf.
Abfrage 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] ]]
Abfrage 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] ]]
Abfrage 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]
Beispiel mit angewendetem Produktfilter
Besser wäre es, die Produktspalte mit einer einzelnen Kennzahlenspalte ohne einen Filter in den Bericht aufzunehmen. Erstellen Sie dann einen Filter, der die erforderlichen Produkte enthält. Wenn Sie die Produkte in verschiedene Kategorien einteilen möchten, verwenden Sie eine CASE
-Anweisung. In diesem Szenario wird eine einzelne MDX-Abfrage mit den gefilterten Zeilen generiert. Obwohl die CASE
-Anweisung von Oracle Analytics angewendet wird, verwendet sie die Teilmenge der Daten und nicht alle Datensätze.
Hier ist ein weiteres Szenario, indem die CASE
-Anweisung Performanceprobleme verursacht.
Ein Entwickler wendet eine CASE
-Anweisung an, um Marken umzubenennen, und Benutzer können über einen Dashboard-Prompt die Marke auswählen.
.jpg
.jpg
Da die CASE
-Anweisung in der MDX nicht unterstützt wird, kann der Filter für Brand2
nicht in der MDX-Abfrage angewendet werden. Alle Marken werden ausgewählt, was nicht optimal ist.
.jpg
In diesem Szenario empfiehlt Oracle, die CASE
-Anweisung zu entfernen und Elemente in der Datenbank umzubenennen oder Aliasnamen zu erstellen.