Réglage des performances des requêtes de base de données multidimensionnelle

Lorsque vous utilisez une base de données multidimensionnelle en tant que source de données dans Oracle Analytics, vous pouvez rencontrer des problèmes de performances, ce qui aboutit à des requêtes MDX (expression multidimensionnelle) générées non optimales.

En modifiant la conception, vous pouvez améliorer les requêtes MDX générées par Oracle Analytics. L'incidence peut être immense, non seulement sur les performances du rapport, mais également sur le volume de ressources utilisées dans la base de données. La façon dont vous utilisez les fonctions prises en charge ou non prises en charge a une grande incidence sur les requêtes MDX générées et, de ce fait, sur les performances.

Chaque cas d'emploi étant unique, l'équipe de développement doit examiner les différentes options, analyser les journaux des requêtes Oracle Analytics et choisir la solution la plus adaptée au cas d'emploi.

Cette rubrique ne traite pas des problèmes de performances causés par l'infrastructure, comme les réseaux, les navigateurs ou la présentation de rapport.

Méthodologie

Oracle recommande d'effectuer les tâches suivantes pour améliorer les performances. Il est important de comprendre la structure des requêtes MDX, ainsi que les journaux des requêtes générés par Oracle Analytics.

  • Simplifiez les requêtes MDX générées.
  • Réduisez le nombre de requêtes MDX générées.
  • Assurez-vous que les sélections et filtres optimaux sont appliqués dans la requête MDX.
  • Réglez les performances avec l'administrateur de base de données sur la base de données multidimensionnelle et vérifiez pourquoi les performances de la base de données source sont toujours insuffisantes.
  • Modifiez l'analyse en fonction des commentaires de l'administrateur de base de données.

Optimisation des étapes de sélection

Lorsque vous optimisez les étapes de sélection, vous pouvez simplifier les requêtes MDX, réduire le nombre de requêtes MDX générées et améliorer les performances.

La figure suivante présente un exemple de comparaison entre des étapes de sélection optimisées et non optimisées.

La description de GUID-43E6F348-B14C-40DC-8C21-DA34DAE44344-default.jpg est la suivante
.jpg

Instructions CASE

La fonctionnalité des instructions CASE n'est pas prise en charge dans les requêtes MDX et doit toujours être appliquée dans Oracle Analytics. La logique décrite dans cette section concernant les instructions CASE est valide pour la plupart des fonctions qui ne sont pas prises en charge dans les requêtes MDX (if null, etc.).

L'utilisation des instructions CASE présente des avantages et des inconvénients. Lorsque vous incluez des instructions CASE dans des formules de rapport, elles ne sont pas incluses dans la requête MDX. La requête MDX peut donc être simplifiée et les performances améliorées. Toutefois, le filtre ne fonctionne pas aussi efficacement, ce qui signifie que la requête peut renvoyer plus d'enregistrements que nécessaire.

L'utilisation de la fonctionnalité des instructions CASE s'accompagne des restrictions suivantes :

  • Si l'instruction CASE ne combine pas plusieurs membres, la colonne de base utilisée dans l'instruction doit être incluse dans la requête et les vues en tant que colonne distincte masquée.
  • Si l'instruction CASE combine plusieurs membres, la colonne de base ne peut pas être incluse dans la vue sans incidence sur le niveau d'agrégation. Dans ce cas :
    • Si la règle d'agrégation de l'indicateur n'est pas Agrégation externe, la colonne de base doit être exclue de la requête.
    • Si la règle d'agrégation de l'indicateur est Agrégation externe, la colonne de base doit être incluse dans la requête et exclue de la vue. Vous devez remplacer la règle d'agrégation de l'indicateur, en passant de la règle par défaut à une règle d'agrégation interne simple (SUM, MAX, MIN). Cette opération fonctionne uniquement si la règle d'agrégation interne est utilisée pour combiner des membres et offre des résultats corrects.

Fonction FILTER

Contrairement à la fonctionnalité des instructions CASE, la fonction FILTER peut être envoyée à la base de données pour exécution.

L'avantage principal de l'utilisation de la fonction FILTER dans les formules de rapport est que la sélection est appliquée dans la requête MDX, et que le volume de données calculées et extraites de la base de données est réduit.

L'inconvénient principal de l'utilisation de la fonction FILTER est qu'elle peut augmenter le nombre de requêtes MDX exécutées. Par défaut, une requête est exécutée pour chaque fonction FILTER utilisée.

Exemple d'utilisation des fonctions CASE et FILTER

Dans cet exemple, un utilisateur demande un rapport indiquant le bénéfice par trimestre et l'unité de gestion des stocks de produit sélectionnée. Par ailleurs, les unités de gestion des stocks sont regroupées dans 12 catégories. Les produits suivants de la gamme sont affectés à la catégorie Other Cola : Cola, Diet Cola et Shared Diet Cola.

La description de GUID-7198F143-54E6-4A48-9579-96624936A94D-default.jpg est la suivante
.jpg

La requête logique avec l'instruction CASE est la suivante :

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

Aucun regroupement n'est effectué selon l'instruction CASE. Une requête MDX simple est générée, avec l'instruction CASE traitée par 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]

L'instruction CASE est exécutée sur le serveur BI Server, ce qui est visible avec le paramètre de base de données défini sur 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]

Vous pouvez également utiliser un filtre sur la mesure de bénéfice pour extraire uniquement les membres requis de la gamme. Dans ce scénario, vous créez trois mesures avec les filtres correspondants appliqués.

La requête logique avec l'instruction FILTER est la suivante :

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

Dans ce scénario, trois requêtes sont générées (une pour chaque filtre) et vous rencontrez des problèmes de performances.

Requête 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]

]]

Requête 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]

]]

Requête 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]

Exemple d'application de filtre de produit

Une meilleure approche consiste à inclure la colonne de produit dans le rapport avec une seule colonne d'indicateur sans filtre. Ensuite, créez un filtre qui inclut les produits requis. Si vous voulez regrouper les produits dans différentes catégories, utilisez une instruction CASE. Dans ce scénario, une requête MDX unique est générée avec les lignes filtrées, et même si l'instruction CASE est appliquée par Oracle Analytics, elle utilise le sous-ensemble de données, et non tous les enregistrements.

Voici un autre scénario dans lequel les instructions CASE provoquent des problèmes de performances.

Un développeur applique une instruction CASE pour renommer les marques, et une invite de tableau de bord permet aux utilisateurs de sélectionner la marque .

La description de GUID-56356AA9-2AF6-4A67-8ADD-FC4F7F70306C-default.jpg est la suivante
.jpg

La description de GUID-E63719C8-9936-412B-8228-F20E8F048C46-default.jpg est la suivante
.jpg

Puisque l'instruction CASE n'est pas prise en charge dans la requête MDX, le filtre sur Brand2 ne peut pas être appliqué dans la requête MDX. Toutes les marques sont sélectionnées, sans optimisation.

La description de GUID-6BE1F274-8257-4E31-8D42-406357A07B2A-default.jpg est la suivante
.jpg

Dans ce type de scénario, Oracle recommande d'enlever l'instruction CASE, et de renommer les membres dans la base de données ou de créer des alias.