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.
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.
.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 :
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.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 :
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.
.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 .
.jpg
.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.
.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.