השוואת ערכים הכוללים Nulls

31/07/2013

אין תגובות

כיצד משווים ערכים? לכאורה בעזרת סימנים כמו = (שווה) או <> (שונה), לא כך?
בואו נבדוק בעזרת טבלה לדוגמה:

If Object_ID('tempdb..#Tmp','U') Is Not Null Drop Table #Tmp;

Create Table #Tmp(I Int Primary Key,

                  A Int Null,

                  B Int Null);

Go

 

Insert

Into    #Tmp

Values  (1,Null,Null),

        (2,10,Null),

        (3,Null,10),

        (4,10,20),

        (5,10,10);

 

Select  *

From    #Tmp;

clip_image002

מבחינת האדם הסביר, בשורות 1 & 5 הערכים זהים, ובשורות 2-4 הערכים שונים.

ומה מבחינת SQL Server?

Select *

From   #Tmp

Where  A<>B;

clip_image004

יוסטון ווי הב אה פרובלם: לדעת המערכת רק הערכים בשורה 4 שונים.

מה הנימוק? Null אינו ערך ולכן לא ניתן להשוות בינו לבין ערכים, וניתן להדגים זאת באחת משתי הדרכים:

1. יש כזו חידה/בדיחה ילדותית "התנין- מהו יותר: ירוק או ארוך?", ולא משנה מה עונים- תמיד ניתן לטעון שההיפך הוא הנכון (כלומר- מחד הוא ארוך גם מבפנים וגם מבחוץ אך ירוק רק מבחוץ, ומאידך הוא ירוק גם לאורך וגם לרוחב אך ארוך רק לאורך).

הפואנטה היא שלא ניתן להשוות בין מה שאינו בר השוואה.

2. אסי קנה 2 כסאות ב-200 ₪ בסה"כ, ובתיה לא קנתה כלום. מי שילם יותר על כל כיסא?

מבחינת סה"כ ההוצאה ברור שאסי הוציא יותר (200 לעומת 0) אך מבחינת המחיר ליחידה – לא ניתן להשוות ולומר מי שילם יותר. אסי שילם ליחידה 200/2=100 ₪ ואילו בתיה 0/0=? ("לא מוגדר" בשפת המתימטיקאים, ו-Null בשפת אנשי המחשבים).

לעצם העניין: Null כמובן אינו 10 אך לא ניתן לומר אם הוא גדול/קטן/שווה/שונה ממנו מכיוון שהוא אינו ערך. כך גם לא ניתן לומר אם Null גדול/קטן/שווה/שונה מ-Null.

יחד עם זאת- אנחנו יודעים בדיוק מה אנחנו רוצים לברר: האם מה שכתוב ב-A הוא מה שכתוב ב-B..

בקיצור, הפתרון הוא זה:

Select *

From   #Tmp

Where  (A Is Null And B Is Not Null)

       Or (A Is Not Null And B Is Null)

       Or A<>B;

clip_image006

או שהראשון אינו Null והשני כן (שורה 2)

או שהראשון Null והשני לא (שורה 3)

או שהם שונים (שורה 4)..

אי אפשר לחשוב על משהו קצת יותר אלגנטי? משהו עם תנאי אחד במקום שלושה?

רמז: הפונקציה NullIf שבעבר השמצתי אותה קשות מחזירה לכאורה ערך Null כשמשווים בעזרתה שני ערכים שווים (הסתייגויות ופתרונות להסתייגויות – בהמשך):

Select  *,

        NullIf(A,B) [NullIf(A,B)],

        NullIf(B,A) [NullIf(B,A)]

From    #Tmp;

clip_image008

אני מציג בשליפה גם את (NullIf(A,B וגם את (NullIf(B,A מכיוון שהפונקציה אינה סימטרית!

שימו לב ש-NullIf מחזירה לפעמים ערך ולפעמים Null, אבל כששני הערכים שונים (2-4) – לפחות אחד המקרים אינו Null!

(מה שמופיע [בסוגריים המרובעיים] זו הכותרת של העמודה בפלט כדי לראות מה החישוב)

כלומר- ניתן לבדוק בעזרת IsNull (אשר שונה מ-NullIf) אם אחד משני ה-NullIf-ים אינו Null:

Select  *,

        NullIf(A,B) [NullIf(A,B)],

        NullIf(B,A) [NullIf(B,A)],

        IsNull(NullIf(A,B),NullIf(B,A)) [IsNull(NullIf(A,B),NullIf(B,A))]

From    #Tmp;

 

Select  *

From    #Tmp

Where   IsNull(NullIf(A,B),NullIf(B,A)) Is Not Null;

clip_image010

ניתן לראות בשליפה הראשונה שבשורות 2-4 חזר ערך בעמודה הימנית,

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

אגב- אם למישהו יש רעיון אלגנטי אחר ואולי אף מוצלח משלי- אשמח לשמוע!

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

כתיבת תגובה

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