Oracle Analitik veri kaynağı olarak çok boyutlu bir veritabanı kullandığında, performans üzerinde büyük etkisi olabilecek ek tasarım faktörleri vardır.
Performans iyileştirme tasarım çözümlerinin kullanım senaryosuna göre değiştiğini anlamak önemlidir. Bu konu size en iyi uygulamaları veya her zaman uygulanması gereken ve her soruna uygun tek bir çözüm sunmayacaktır. Bunun yerine analizlerinizin ve oluşturulan kodunuzun performansını artırmanıza yardımcı olacak ayarlama yöntemleri ve teknikleri sunuyoruz.
Seçenekleri gözden geçirmek, Oracle Analitik sorgu günlüklerini analiz etmek ve kullanım senaryonuz için en iyi çözümü seçmek geliştirme ekibinin sorumluluğundadır.
Bu konu; ağlar, tarayıcılar veya rapor sunumu gibi altyapınızın neden olduğu performans sorunlarını ele almaz.
Metodoloji
Performansı artırmak için aşağıdaki görevleri tamamlamanızı öneririz. Bu görevlerin bir ön koşulu olarak, çok boyutlu ifade (MDX) sorgu yapısının yanı sıra Oracle Analitik'in oluşturduğu sorgu günlüklerini anlamak önemlidir. Temel görevler şunlardır:
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.
Örnek:
CASE
Komutları
CASE
komutu fonksiyonelliği MDX'te desteklenmez ve bunun her zaman Oracle Analitik'te uygulanması gerekir. Aşağıda CASE
komutlarıyla ilgili olarak açıklanan mantık, MDX'te desteklenmeyen çoğu fonksiyon için geçerlidir (if null
vb.).
CASE
komutlarını kullanmanın artıları ve eksileri vardır. Rapor formüllerine CASE
komutlarını eklediğinizde bunlar MDX'e 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.
Gördüğünüz gibi her kullanım senaryosu benzersizdir. Temel amaç, MDX sorgularını basitleştirmek ve aynı zamanda en uygun filtreleri ve seçimleri uygulamaktır.
CASE
komutu fonksiyonelliğinin kullanımına ilişkin kısıtlamalar vardı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
fonksiyonunun aksine, FILTER
fonksiyonu yürütülmek üzere veritabanına gönderilebilir.
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.Her kullanım senaryosunun benzersiz olduğunu unutmayın. Amaç, MDX sorgularını basitleştirmek ve aynı zamanda en uygun filtreleri ve seçimleri uygulamaktır.
CASE
ve FILTER
Karşılaştırma Senaryosu
Aynı senaryoya, CASE
fonksiyonelliğine karşı FILTER
fonksiyonelliğini kullanmanın sonuçlarıyla bakmaya devam edelim.
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 on iki 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
ifadesini temel alan bir gruplama yoktur, Oracle Analitik tarafından işlenen CASE
komutuyla basit bir MDX oluşturulur. Oluşturulan 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]
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]
Filtre Komutu Senaryosu
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ığı 3 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 Senaryo
Ü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ılabilir. Bu senaryoda, filtrelenen satırlarla oluşturulan tek bir MDX sorgusu olacak ve CASE
komutu Oracle Analitik tarafından uygulansa bile tüm kayıtları değil, verilerin alt kümesini kullanacaktır.
CASE
komutlarının performans sorunlarına neden olduğu başka bir senaryoya bakalım.
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 sorgusuna uygulanamaz. Tüm markalar seçilidir ve bu optimize edilmemiştir.
Bu senaryoda CASE
komutunu kaldırmanızı ve veritabanındaki üyeleri yeniden adlandırmanızı ya da diğer adlar oluşturmanızı öneririz.
Veri kaynağı olarak çok boyutlu bir veritabanı kullandığınızda Oracle Analitik'te performans sorunları yaşayabilirsiniz ve bu da MDX'in ideal olmayan sorgularla sonuçlanmasına neden olabilir. 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. MDX'te desteklenen veya desteklenmeyen fonksiyonları nasıl kullandığınıza dikkat edin çünkü bu, oluşturulan MDX sorgularını ve performansı büyük ölçüde etkiler.