Oracle Analitik'te veri kaynağı olarak çok boyutlu bir veritabanı kullandığınızda performans sorunları yaşayabilirsiniz ve bu da ideal olmayan çok boyutlu ifade (MDX) sorgularının oluşturulmasına neden olur.
Tasarımı değiştirerek Oracle Analitik'in oluşturduğu MDX sorgularını iyileştirebilirsiniz. Bunun sadece rapor performansınız üzerinde değil, aynı zamanda veritabanında kullanılan kaynakların hacmi üzerinde de büyük etkisi olabilir. Desteklenen veya desteklenmeyen fonksiyonları nasıl kullandığınız, oluşturulan MDX sorgularını ve dolayısıyla performansı büyük ölçüde etkiler.
Her kullanım senaryosu benzersiz olduğundan, geliştirme ekibiniz seçenekleri gözden geçirmeli, Oracle Analitik sorgu günlüklerini analiz etmeli ve kullanım senaryonuz için en iyi çözümü seçmelidir.
Bu konu; ağlar, tarayıcılar veya rapor sunumu gibi altyapınızın neden olduğu performans sorunlarını ele almaz.
Metodoloji
Oracle performansı artırmak için aşağıdaki görevleri gerçekleştirmenizi önerir. Oracle Analitik'in oluşturduğu sorgu günlüklerinin yanı sıra MDX sorgu yapısını da anlamanız önemlidir.
Seçim Adımları Optimizasyonu
Seçim adımlarını optimize ettiğinizde MDX sorgularını basitleştirebilir, oluşturulan MDX sorgularının sayısını azaltabilir ve performansı artırabilirsiniz.
Aşağıdaki şekil, optimize edilmiş ve optimize edilmemiş seçim adımlarının karşılaştırılmasının bir örneğini gösterir.
CASE Komutları
CASE
komutu fonksiyonelliği MDX sorgularında desteklenmez ve bunun her zaman Oracle Analitik'te uygulanması gerekir. CASE
komutlarıyla ilgili olarak açıklanan mantık, MDX sorgularında desteklenmeyen çoğu fonksiyon için geçerlidir (if null
vb.).
CASE
komutlarını kullanmanın avantajları ve dezavantajları vardır. Rapor formüllerine CASE
komutlarını eklediğinizde bunlar MDX sorgusuna dahil edilmez. Bu, MDX sorgusunu basitleştirebilir ve performansı artırabilir. Ancak bunun sonucunda etkili bir şekilde filtreleme yapamazsınız ve bu, sorgunun gerekenden daha fazla kayıt döndürebileceği anlamına gelir.
CASE
komutu fonksiyonelliğinin kullanımına ilişkin kısıtlamalar şunlardır:
CASE
komutu birden fazla üyeyi birleştirmiyorsa, komutta kullanılan temel sütunun sorguya ve görünümlere ayrı bir gizli sütun olarak dahil edilmesi gerekir.CASE
komutu birden fazla üyeyi birleştiriyorsa, temel sütun toplama düzeyini etkilemeden görünüme dahil edilemez. Bu durum geçerliyse:
SUM
, MAX
, MIN
) dönüştürmelisiniz. Bu sadece üyeleri birleştirmek için dahili toplama kuralı kullanıldığında ve doğru sonuçlar sağladığında işe yarar.FILTER Fonksiyonu
CASE
komutu fonksiyonelliğinin aksine, FILTER
fonksiyonu yürütülmek üzere veritabanına gönderilebilir.
Rapor formüllerinde FILTER
fonksiyonunu kullanmanın temel avantajı, seçimin MDX sorgusunda uygulanması ve veritabanından hesaplanıp alınan veri hacminin azaltılmasıdır.
FILTER
fonksiyonunu kullanmanın temel dezavantajı, yürütülen MDX sorgularının sayısını artırabilmesidir. Öndeğer olarak, kullanılan her FILTER
fonksiyonu için bir sorgu yürütülür.
CASE - FILTER Karşılaştırma Örneği
Bu örnekte, kullanıcı üç aylık döneme göre karı ve seçili ürün stok tutma birimini gösteren bir rapor ister. Ayrıca stok tutma birimleri 12 kategoride gruplanmıştır. Diğer Kola kategorisine şu büyük nesnenin ürünleri atanmıştır: Kola, Diyet Kola ve Paylaşılan Diyet Kola.
CASE
komutu mantıksal sorgusu:
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
CASE
komutunu temel alan bir gruplama yok. Oracle Analitik tarafından işlenen CASE
komutuyla basit bir MDX sorgusu oluşturulur:
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]
CASE
komutu İş Zekası Sunucusu'nda yürütülür ve bu veritabanı ayarının database 0:0,0
olarak ayarlanmasıyla görülür:
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]
Alternatif olarak, sadece gerekli büyük nesne üyelerini almak için kar metriğine göre bir filtre kullanabilirsiniz. Bu senaryoda ilgili filtrelerin uygulandığı üç metrik oluşturursunuz.
FILTER
komutu mantıksal sorgusu:
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
Bu senaryoda her filtre için bir adet olmak üzere üç sorgu oluşturulur ve performans sorunlarıyla karşılaşırsınız.
Sorgu 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] ]]
Sorgu 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] ]]
Sorgu 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]
Ürün Filtresi Uygulanan Örnek
Ürün sütununu filtre olmadan tek bir ölçü sütunuyla rapora dahil etmek daha iyi bir yaklaşımdır. Daha sonra gerekli ürünleri içeren bir filtre oluşturun. Ürünleri farklı kategoriler halinde gruplamak istiyorsanız CASE
komutu kullanın. Bu senaryoda, filtrelenen satırlarla tek bir MDX sorgusu oluşturulur ve CASE
komutu Oracle Analitik tarafından uygulansa bile tüm kayıtları değil, verilerin alt kümesini kullanır.
CASE
komutlarının performans sorunlarına neden olduğu başka bir senaryo aşağıda verilmiştir.
Bir geliştirici, markaları yeniden adlandırmak için bir CASE
komutu uygular ve bir kumanda tablosu bilgi istemi kullanıcıların markayı seçmesini sağlar.
CASE
komutu MDX'te desteklenmediğinden, Brand2
üzerindeki filtre MDX sorgusunda uygulanamaz. Tüm markalar seçilidir ve bu optimize edilmemiştir.
Bu senaryo tipinde, Oracle CASE
komutunu kaldırmanızı ve veritabanındaki üyeleri yeniden adlandırmanızı ya da diğer adlar oluşturmanızı önerir.