כוונון ביצועים של שאילתות מסד נתונים רב-ממדי

כאשר אתה משתמש במסד נתונים רב-ממדי כמקור נתונים ב-Oracle Analytics, אתה עלול להיתקל בבעיות ביצועים שגורמות ליצירת שאילתות לא אופטימליות של ביטוי רב-ממדי (MDX).

על-ידי שינוי העיצוב, תוכל לשפר את שאילתות ה-MDX המופקות ב-Oracle Analytics. לפעולה זו עשויה להיות השפעה עצומה, לא רק על ביצועי הדוחות שלך אלא גם על נפח המשאבים שבהם נעשה שימוש במסד הנתונים. אופן השימוש בפונקציות נתמכות או לא נתמכות משפיע מאוד על שאילתות ה-MDX שנוצרות ולכן על הביצועים.

מכיוון שכל תרחיש שימוש הוא ייחודי, על צוות הפיתוח לבחון אפשרויות אלו, לנתח את יומני השאילתות של Oracle Analytics, ולבחור את הפתרון הטוב ביותר עבור תרחיש השימוש שלך.

נושא זה לא מתייחס לבעיות ביצועים שנגרמו בגלל התשתית שלך, כגון דפדפני הרשת או מצגת הדוח.

מתודולוגיה

Oracle ממליצה לבצע את המשימות הבאות כדי להגביר את הביצועים. חשוב שתבין את מבנה השאילתות של MDX וכן את יומני השאילתות ש-Oracle Analytics מייצרת.

  • פשט את שאילתות ה-MDX המופקות.
  • הפחתת מספר שאילתות ה-MDX שהופקו.
  • הקפדה על החלה של מסננים ובחירות אופטימליים בשאילתת ה-MDX.
  • כוונון ביצועים עם מנהלן מסד הנתונים (DBA) במסד הנתונים הרב-ממדי ובדיקה מדוע מסד הנתונים במקור עדיין מבצע גרוע.
  • שינוי הניתוח במבוסס על משוב של DBA.

אופטימיזציה של שלבי הבחירה

כאשר תבצע אופטימיזציה של שלבי בחירה תוכל לפשט את שאילתות ה-MDX, להפחית את מספר שאילתות ה-MDX המופקות, ולהגביר את הביצועים.

האיור הבא מציג דוגמה להשוואה של שלבי בחירה אופטימליים ולא אופטימליים.

להלן תיאור של GUID-43E6F348-B14C-40DC-8C21-DA34DAE44344-default.jpg
.jpg

משפטי CASE

הפונקציונליות של משפט CASE אינה נתמכת בשאילתות MDX ויש תמיד להחיל אותה ב-Oracle Analytics. הלוגיקה המוסברת בפרק זה לגבי משפטי CASE תקפה לרוב הפונקציות שאינן נתמכות בשאילתות MDX (אם null, וכן הלאה).

ישנם יתרונות וחסרונות בעת שימוש במשפטי CASE. כאשר אתה כולל משפטי CASE בנוסחאות דוח, הם לא נכללות בשאילתת MDX. זה יכול לפשט את שאילתת ה-MDX ולשפר את הביצועים. עם זאת, הפשרה היא שאינך יכול לסנן ביעילות רבה, מה שאומר שהשאילתא עשויה להחזיר יותר רשומות מהנדרש.

להלן הגבלות ל שימוש בפונקציונליות של משפט CASE:

  • אם משפט ה-CASE לא משלב מספר איברים, עמודת הבסיס המשמשת במשפט חייבת להיות כלולה בשאילתא ובתצוגות כעמודה נפרדת נסתרת.
  • אם משפט ה-CASE משלב מספר איברים, אי אפשר לכלול את עמודת הבסיס בתצוגה מבלי להשפיע על רמת הסכימה. במקרה זה:
    • אם כלל הסכימה של המדד אינו סכימה חיצונית, אין לכלול את עמודת הבסיס בשאילתא.
    • אם כלל הסכימה של מדד הוא סכימה חיצונית, עמודת הבסיס חייבת להיכלל בשאילתא ולא להיכלל בתצוגה. עליך לשנות את כלל הסכימה של המדד מברירת המחדל שלו לכלל סכימה פנימי פשוט (SUM, MAX, MIN). זה עובד רק אם כלל הסכימה הפנימי משמש לשילוב איברים ונותן תוצאות נכונות.

פונקציית FILTER

שלא כמו משפט הCASE, ניתן לשלוח את פונקציית הFILTER למסד הנתונים לצורך ביצוע.

היתרון העיקרי של שימוש בפונקציית הFILTER בנוסאות דוח הוא שהבחירה מיושמת בשאילתת ה-MDX ונפח הנתונים המחושבים ומאוחזרים ממסד הנתונים מצטמצם.

החיסרון העיקרי של שימוש בפונקציית הFILTER הוא שהיא עשויה להגדיל את מספר שאילתות MDX שיבוצעו. לפי ברירת המחדל, מתבצעת שאילתא אחת לכל פונקציית FILTER שבה נעשה שימוש.

דוגמה של CASE לעומת FILTER

בדוגמה זו, המשתמש מבקש דוח שמראה רווח לפי רבעון ויחידת שמירת מלאי של מוצר נבחר. בנוסף, יחידות שמירת המלאי מקובצות יחד בשתיים-עשרה קטגוריות. לקטגוריה "קולה אחרת" הוקצו המוצרים הבאים של תחום פעילות עסקית: קולה, קולה דיאט, קולה דיאט משותפת.

להלן תיאור של GUID-7198F143-54E6-4A48-9579-96624936A94D-default.jpg
.jpg

הנה השאילתא הלוגית של משפט CASE:

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. נוצרת שאילתת MDX פשוטה, כאשר NAPY CASE מעובד על-ידי 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]

משפט הCASE מתבצע ב-BI Server ואת זה ניתן לראות לפי הגדרת מסד הנתונים שנקבעה על 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]

לחלופין, תוכל להשתמש במסנן כנגד מדד הרווח כדי לאחזר את איברי תחום הפעילות העסקית בלבד. בתרחיש זה, יוצרים 3 מדדים כשמחילים את המסננים המתאימים.

הנה השאילתא הלוגית של משפט FILTER:

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

בתרחיש זה מופקות שלוש שאילתות, אחת לכל מסנן, ואתה חווה בעיות בביצועים:

שאילתא 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]

]]

שאילתא 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]

]]

שאילתא 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]

דוגמה של 'הוחל מסנן מוצרים'

גישה טובה יותר היא לכלול את עמודת המוצר בדוח עם עמודת מדד יחידה ללא מסנן. לאחר מכן צור מסנן שכולל את המוצרים הדרושים. אם תרצה לקבץ את המוצרים בקטגוריות אחרות, השתמש במשפט CASE. בתרחיש זה, תופק שאילתת MDX יחידה עם השורות שסוננו ולמרות ש-Oracle Analytics יחיל את משפט הCASE, המשפט יעשה שימוש בתת-סל של נתונים ולא בכל הרשומות.

הנה תרחיש אחר שבו משפטי CASE גורמים לבעיות בביצועים.

מפתח מחיל משפטCASE כדי לשנות שמות מותגים, ומנחה לוח המידע מרשה למשתמשים לבחור את המותג.

להלן תיאור של GUID-56356AA9-2AF6-4A67-8ADD-FC4F7F70306C-default.jpg
.jpg

להלן תיאור של GUID-E63719C8-9936-412B-8228-F20E8F048C46-default.jpg
.jpg

כיוון שמשפט הCASE אינו נתמך ב-MDX, לא ניתן להחיל את המסנן על Brand2 בשאילתת ה-MDX. נבחרים כל המותגים, וזה לא ממוטב.

להלן תיאור של GUID-6BE1F274-8257-4E31-8D42-406357A07B2A-default.jpg
.jpg

בסוג זה של תרחיש, Oracle ממליצה להסיר את משפט ה-CASE ולשנות את שמות האיברים במסד הנתונים או ליצור כינויים.