Çok Boyutlu Veritabanı Sorgularının Performansını Ayarlama

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:

  • Oluşturulan MDX'i basitleştirin.
  • Oluşturulan MDX sorgularının sayısını azaltın.
  • MDX'te en uygun filtrelerin ve seçimlerin uygulandığına emin olun.
  • Çok boyutlu veritabanı tarafında veritabanı yöneticisiyle performans ayarı yapın ve kaynak veritabanının hala kötü performans gösterme nedenini doğrulayın.
  • Analizi veritabanı yöneticisinin geri bildirimine göre değiştirin.

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:

GUID-43E6F348-B14C-40DC-8C21-DA34DAE44344-default.jpg açıklaması aşağıdadır
.jpg'' çiziminin açıklaması

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:
    • Ölçünün toplama kuralı Harici Toplama değilse temel sütunun sorgudan hariç tutulması gerekir.
    • Ölçünün toplama kuralı Harici Toplama ise temel sütunun sorguya dahil edilmesi ve görünümden hariç tutulması gerekir. Ölçünün toplama kuralını öndeğerden basit bir dahili toplama kuralına (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.

  • 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.

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.

GUID-7198F143-54E6-4A48-9579-96624936A94D-default.jpg açıklaması aşağıdadır
.jpg'' çiziminin açıklaması

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:

GUID-56356AA9-2AF6-4A67-8ADD-FC4F7F70306C-default.jpg açıklaması aşağıdadır
.jpg'' çiziminin açıklaması

GUID-E63719C8-9936-412B-8228-F20E8F048C46-default.jpg açıklaması aşağıdadır
.jpg'' çiziminin açıklaması

CASE komutu MDX'te desteklenmediğinden, 'Brand2' üzerindeki filtre MDX sorgusuna uygulanamaz. Tüm markalar seçilidir ve bu optimize edilmemiştir.

GUID-6BE1F274-8257-4E31-8D42-406357A07B2A-default.jpg açıklaması aşağıdadır
.jpg'' çiziminin açıklaması

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.