Union לעומת Or

06/07/2013

אין תגובות

מה יותר טוב – Union או Or?
רגע, איזו מין שאלה זו? Union זה Union ו-Or זה Or.. מה הקשר?
הקשר הוא שבמקום לכתוב תנאי Or אפשר לכאורה לבצע Union בין השורות שעונות על התנאי הראשון והשורות שעונות על התנאי השני. לא ככה?
אה.. יש בזה משהו, אבל זה לא עובד הפוך: אם נעשה Union בין שתי טבלאות שונות, לא נוכל להמיר אותו ב-Or.
כשחושבים על זה – זה נשמע נכון, אבל בכל זאת- מה קורה כשכן?
בואו ננסה, וניצור קודם כל טבלה מתאימה:

Use tempdb;

Go

 

If Object_ID('T_Messages','U') Is Not Null Drop Table T_Messages;

 

Select *

Into   T_Messages

From   sys.messages;

 

Select *

From   T_Messages;

clip_image002

וכעת לקודים, כשאנחנו מקפידים להשתמש ב-Union All שאינו מבצע Distinct בהיחבא:

Select  language_id,

        message_id,

        Case When language_id=1033 Then severity Else message_id End MyData

From    T_Messages

Where   language_id=1033 Or is_event_logged=1;

 

Select  language_id,

        message_id,

        severity MyData

From    T_Messages

Where   language_id=1033

Union All

Select  language_id,

        message_id,

        message_id

From    T_Messages MyData

Where   is_event_logged=1;

clip_image004

ומה מבחינת הביצועים:

clip_image006

ברור: השאילתה הראשונה מבצעת Scan בודד על הטבלה, ואילו השנייה שניים; ולכן ברור שהמחיר של ה-Union במקרה זה כפול; אלא שיש לשים לב שהשליפות אינן מחזירות סטים זהים:

clip_image008

(הורץ על SQL Server 2008 R2)

מתברר שהראשונה מחזירה פחות שורות.. האם בכל זאת היינו צריכים להשתמש ב-Union שמבצע Distinct? לא, זו אינה הבעייה: ה-Or מחזיר כל שורה שעונה על שני התנאים פעם אחת בלבד ועם הערך של עמודה severity, בניגוד ל-Union שמחזיר כל שורה שעונה על שני התנאים פעמיים – פעם עם הערך של severity ופעם עם הערך של message_id, וכך ה-Distinct לא יועיל.

מה שכן יעזור זה להוסיף בשליפה התחתונה של ה-Union תנאי שישלוף רק את השורות שעונות על התנאי השני ולא על הראשון:

Select  language_id,

        message_id,

        severity MyData

From    T_Messages

Where   language_id=1033

Union All

Select  language_id,

        message_id,

        message_id

From    T_Messages MyData

Where   is_event_logged=1

        And language_id<>1033;

במקרה זה השליפה מחזירה 16016 שורות, כמו שליפת ה-Or; אך בכל מקרה – ה-Scan הכפול הופך אותה ללא כדאית.

אם כך- מה נעשה במקרה ההפוך? יש לנו שליפת Union רגילה (לא זו "המשופרת" שפותרת את הבעייה) ואנחנו מעוניינים להחליף אותה בשליפת Or שתבצע Scan אחד במקום שניים.

זה קצת מסובך כי במקרה זה נצטרך למצוא דרך לשלוף כל שורה שעונה על שני התנאים – פעמיים.

דרך אפשרית לעשות זאת הוא להתחכם ולהשתמש בפטנט שעושה שימוש באופרטור Values:

Select  language_id,

        message_id,

        MyData

From    T_Messages

        Cross Apply (Select S MyData From (Values(Case When language_id=1033 Then severity Else Null End),

                                                 (Case When is_event_logged=1 Then message_id Else Null End)) As D(S)) Mx

Where   (language_id=1033 Or is_event_logged=1)

        And MyData Is Not Null;

clip_image010

השימוש ב-Values ב-Cross Apply הופך את שני הערכים משתי העמודות לסט של שתי שורות,

ובפסוקית Where אפשר לסנן את ערכי ה-Null שאינם עונים על התנאים והצטרפו בזכות שכניהם שכן עונים על אחד התנאים, וכך חוזרות 16721 שורות.

ומה עם הביצועים?

clip_image012

בהחלט כדאי: יש עלות מסויימת לשימוש ב-Values, אך עדיין – החסכון ב-Scan שווה זאת.

לסיכום: כשיש לנו פקודת Union שפונה פעמיים לאותה טבלה – יתכן מאוד שניתן יהיה להחליף אותה בשליפת Or יעילה יותר, רק לשים לב שהמעבר אינו ברור מאליו וצריך לברר האם ואיך מחזירים סט זהה למקורי.

הוסף תגובה
facebook linkedin twitter email

כתיבת תגובה

האימייל לא יוצג באתר. שדות החובה מסומנים *