Performance von multidimensionalen Datenbankabfragen optimieren

Wenn Oracle Analytics eine multidimensionale Datenbank als Datenquelle verwendet, sollten Sie weitere Designaspekte berücksichtigen, die sich enorm auf die Performance auswirken könnten.

Beachten Sie, dass Designlösungen zur Performanceverbesserung je nach Anwendungsfall variieren. Sie erhalten in diesem Thema keine Best Practices oder allgemeingültige Lösungen für alle Situationen. Stattdessen werden hier Optimierungsmethoden und -verfahren erläutert, mit denen Sie die Performance von Analysen und generiertem Code steigern können.

Das Entwicklungsteam muss 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

Wir empfehlen, dass Sie die folgenden Aufgaben ausführen, um die Performance zu verbessern. Diese Aufgaben setzen ein Verständnis der Abfragestruktur für multidimensionale Ausdrücke (MDX) sowie der von Oracle Analytics generierten Abfragelogs voraus. Hauptaufgaben:

  • Vereinfachen Sie die generierte MDX.
  • Reduzieren Sie die Anzahl der generierten MDX-Abfragen.
  • Stellen Sie sicher, dass optimale Filter und Auswahloptionen in der MDX angewendet wurden.
  • Optimieren Sie die Performance auf Seite der multidimensionalen Datenbank mit dem Datenbankadministrator (DBA), und überprüfen Sie, warum die Quelldatenbank noch eine mangelhafte Performance aufweist.
  • Ändern Sie die Analyse basierend auf dem Feedback des DBA.

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.

Beispiel:

Beschreibung von GUID-43E6F348-B14C-40DC-8C21-DA34DAE44344-default.jpg folgt
.jpg

CASE-Anweisungen

CASE-Anweisungsfunktionalität wird in MDX nicht unterstützt und muss immer in Oracle Analytics angewendet werden. Die unten erläuterte Logik in Bezug aufCASE-Anweisungen gilt für die meisten Funktionen, die in MDX nicht unterstützt werden (if null usw.).

Die Verwendung von CASE-Anweisungen bringt Vor- und Nachteile mit sich. Wenn Sie CASE-Anweisungen in Berichtsformeln einschließen, werden sie nicht in die MDX 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.

Wie Sie sehen ist jeder Anwendungsfall einzigartig. Hauptziel ist es, die MDX-Abfragen zu vereinfachen und gleichzeitig optimale Filter und Auswahloptionen anzuwenden.

Die Verwendung der CASE-Anweisungsfunktionalität ist wie folgt eingeschränkt:

  • Wenn die 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.
  • Wenn die CASE-Anweisung mehrere Elemente kombiniert, kann die Basisspalte nicht ohne Auswirkungen auf die Aggregationsebene in die Ansicht aufgenommen werden. In diesem Fall gilt Folgendes:
    • Wenn die Aggregationsregel der Kennzahl nicht Externe Aggregation lautet, muss die Basisspalte aus der Abfrage ausgeschlossen werden.
    • Wenn die Aggregationsregel der Kennzahl Externe Aggregation lautet, muss die Basisspalte in die Abfrage aufgenommen und aus der Ansicht ausgeschlossen werden. Sie müssen die Aggregationsregel der Kennzahl vom Standardwert in eine einfache interne Aggregationsregel ändern (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-Anweisung 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.

Denken Sie daran, jeder Anwendungsfall ist einzigartig. Ziel ist es, MDX-Abfragen zu vereinfachen und gleichzeitig optimale Filter und Auswahloptionen anzuwenden.

CASE-Szenario und FILTER-Szenario

Sehen wir uns nun das gleiche Szenario mit den Ergebnissen der CASE-Funktionalität gegenüber der FILTER-Funktionalität an.

Der Benutzer fordert 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.

Beschreibung von GUID-7198F143-54E6-4A48-9579-96624936A94D-default.jpg folgt
.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 basierend auf derCASE-Anweisung. Eine einfache MDX-Abfrage wird generiert, und die CASE-Anweisung wird von Oracle Analytics verarbeitet. Die generierte 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]

Die CASE-Anweisung wird auf dem BI-Server ausgeführt, wie an der Datenbankeinstellung "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]

Szenario mit FILTER-Anweisung

Alternativ dazu können Sie einen Filter für die Gewinnmetrik verwenden, um nur die erforderlichen Geschäftsbereichselemente abzurufen. In diesem Szenario erstellen Sie 3 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]

Szenario mit angewendetem Produktfilter

Besser wäre es, die Produktspalte mit einer einzelnen Kennzahlspalte 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, können Sie eine CASE-Anweisung verwendet. 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.

Sehen wir uns nun ein weiteres Szenario an, in dem 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:

Beschreibung von GUID-56356AA9-2AF6-4A67-8ADD-FC4F7F70306C-default.jpg folgt
.jpg

Beschreibung von GUID-E63719C8-9936-412B-8228-F20E8F048C46-default.jpg folgt
.jpg

Da die CASE-Anweisung in 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.

Beschreibung von GUID-6BE1F274-8257-4E31-8D42-406357A07B2A-default.jpg folgt
.jpg

In diesem Szenario wird empfohlen, die CASE-Anweisung zu entfernen und Elemente in der Datenbank umzubenennen oder Aliasnamen zu erstellen.

Wenn Sie eine multidimensionale Datenbank als Datenquelle verwenden, treten unter Umständen Performanceprobleme in Oracle Analytics auf, aufgrund derer suboptimale 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. Achten Sie darauf, wie Sie unterstützte oder nicht unterstützte Funktionen in MDX verwenden, da diese Verwendung enorme Auswirkungen auf die generierten MDX-Abfragen und die Performance hat.