טעויות בתחום ניתוח נתונים קורות לכולם, כל הזמן, ללא קשר לוותק שלכם כדאטה אנליסטס. הבעיה האמיתית מתחילה עם טעויות שאנחנו לא יודעים שבכלל עשינו. אנליסטים ג'וניורים, בהיותם חדשים בתחום ניתוח הנתונים, חשופים מאוד לאפקט 'דאנינג קרוגר' – הטייה קוגנטיבית נפוצה, שבה אנחנו חושבים שאנחנו מתמצאים בתחום הרבה יותר ממה שאנחנו באמת מתמצאים בו. בעיני ג'וניור, SQL יכולה להיראות כשפה מאוד פשוטה straight forward. בהיותה שפה דקלרטיבית, היא עלולה ליצור לסיטואציות של חוסר הבנה בין המשתמש (כלומר, אתם) לבין המחשב. במקרה הטוב זה יביא להרבה בלבול ("למה לעזאזל יש בטבלה הזאת פי 5 שורות ממה שציפיתי שיהיו?") במקרה הרע זה עלול להביא הצגה מסולפת של המציאות.
המטרה של סדרת הפוסטים זו, היא להסביר את הניואנסים שקשורים לדרך שבה SQL עובד. הנושא הזה בדר"כ לא עובר בקורסי SQL מסודרים כי הוא נחשב נישתי. בפועל, אלה הבעיות הכי יומיומיות איתן מתמודד דאטה אנליסט. לרוב לומדים את זה בדרך הקשה אחרי שעשינו כמה טעויות בעבודה, אבל אפשר וגם רצוי, ללמוד מטעויות של אחרים. כמובן שלא תמצאו כאן רשימה ממצה של כל הטעויות שאתם יכולים לעשות, אבל לכל הפחות אפשר להתחיל לפתח מודעות לטעויות אפשריות, מה שיעזור להימנע מהן.
אתמקד בשני נושאים מרכזיים שנחשבים מועדים לטעויות של אנליסטים. פוסט זה יעסוק בטעויות הקשורות לערכי Null. הפוסט הבא יעסוק בטעויות נפוצות שקשורות לJoins.
- עבודה עם Null Values
- טעות #1 – שימוש-בcomparative-operators על Null
- טעות #2 – ביצוע פעולה חישובית על Null
- טעות #3 – אגרגציה ללא התייחסות לNulls
- Take Home Message
עבודה עם Null Values
אחד הנושאים המבלבלים ביותר בעבודה עם דאטה קשור לעבודה עם ערכי Null. לא אלאה אתכם בפרטים של של איך ולמה נוצר הקונספט של ערך Null (למרות שזה ממש מעניין), אבל מסיבות רבות ישנה הסכמה כי Null כקונספט, יוצר המון בעיות. מה שחשוב להכיר הוא שNull אינו Data Type אמיתי. למעשה אפשר להסתכל עליו כNon-Value.

Null מייצג שדה ריק – הוא לא מייצג '0' וגם לא מייצג blank space. בואו נראה איזה בעיות זה עלול ליצור לנו.
טעות #1 – שימוש בComparative Operators על Null
כאנליסטים יוצא לנו לעשות השוואות על שדות מסוג String וכן על שדות מספריים (Int, float וכו') באמצעות '>', '<' או '='. מכיוון שאמרנו שNull הוא Non Value, לא 0 ולא blank space, אי אפשר להשוות אליו ערך אחר (אגב, גם אי אפשר להשוות Null לNull 🤯). הטעות הכי חמורה של אי התייחסות לעמודה שמכילה Null תיווצר בפקודת WHERE. אסביר זאת עם כמה דוגמאות.
נתונה הטבלה הבאה של לקוחות החברה:
SELECT *
FROM customers
AGE | ADDRESS | LAST_NAME | FIRST_NAME | ID_NUMBER |
---|---|---|---|---|
19 | Tel Aviv | Gur | Roey | 301524646 |
22 | Haifa | Barak | David | 217352689 |
25 | Haifa | Cohen | Noa | 102354646 |
25 | Tel Aviv | Feldman | Nadav | 301857456 |
33 | Null | Moshe | Dafna | 105235689 |
Null | Null | Levy | Itay | 201567842 |
אם אנסה לחפש בDB את לקוחות החברה שהכתובת שלהם אינה Tel Aviv, יתכן ואכתוב שאילתא כזו:
SELECT *
FROM customers
WHERE [address] <> 'Tel Aviv'
AGE | ADDRESS | LAST_NAME | FIRST_NAME | ID_NUMBER |
---|---|---|---|---|
22 | Haifa | Barak | David | 217352689 |
25 | Haifa | Cohen | Noa | 102354646 |
כפי שאתם רואים סיננתי בטעות שני לקוחות שכתובתם הייתה Null – זאת משום שSQL מתעלם מכל סוג של Comparison Operations על Null (מאחורי הקלעים, הוא מתייחס לזה כUnkonwn ולא מציג את התוצאה).
שאילתא נכונה יותר הייתה צריכה לכלול התייחסות לערכי Null:
SELECT *
FROM customers
WHERE [address] <> 'Tel Aviv' OR [address] IS NULL
AGE | ADDRESS | LAST_NAME | FIRST_NAME | ID_NUMBER |
---|---|---|---|---|
22 | Haifa | Barak | David | 217352689 |
25 | Haifa | Cohen | Noa | 102354646 |
33 | Null | Moshe | Dafna | 105235689 |
Null | Null | Levy | Itay | 201567842 |
אותו קונספט חל על השוואת Null לכל דבר אחר. הנה דוגמא של השוואה על שדה מספרי באותה טבלה (Int):
SELECT *
FROM customers
WHERE AGE > 20
AGE | ADDRESS | LAST_NAME | FIRST_NAME | ID_NUMBER |
---|---|---|---|---|
22 | Haifa | Barak | David | 217352689 |
25 | Haifa | Cohen | Noa | 102354646 |
25 | Tel Aviv | Feldman | Nadav | 301857456 |
33 | Null | Moshe | Dafna | 105235689 |
כפי שניתן לראות, שני ערכים סוננו מחוץ לטבלה: רועי גור בן ה19, ואיתי לוי שגילו בטבלה הוא Null. השוואה לNull איננה אפשרית כי Null אינו data type "אמיתי" ולכן SQL התעלם מתוצאה זו.
טעות #2 – ביצוע פעולה חישובית על Null
כל פעולה חישובית (חיבור, חיסור, חילוק, הכפלה) על ערך Null אינה אפשרית משום שהוא Non-Value. לשם הדוגמא, Null * 10 לא שווה 10, אלא Null.
SQL יחזיר פעולת חישוב על Nulll כNull. למה? כי Null מייצג ערך לא קיים.
10 * כלום = כלום.
אם נחזור לטבלת הדוגמא שלנו, חישוב על ערך Null יראה כך:
SELECT ID, FIRST_NAME, LAST_NAME, AGE, AGE * 10
FROM customers
AGE | AGE | ADDRESS | LAST_NAME | FIRST_NAME | ID_NUMBER |
---|---|---|---|---|---|
190 | 19 | Tel Aviv | Gur | Roey | 301524646 |
220 | 22 | Haifa | Barak | David | 217352689 |
250 | 25 | Haifa | Cohen | Noa | 102354646 |
250 | 25 | Tel Aviv | Feldman | Nadav | 301857456 |
330 | 33 | Null | Moshe | Dafna | 105235689 |
Null | Null | Null | Levy | Itay | 201567842 |
טעות #3 – אגרגציה ללא התייחסות לNulls
כפי שאמרנו (שוב ושוב 😉 ), מבחינת המחשב שלכם, Null מייצג שדה ריק. כשנעשה אגרגציה, SQL יפיל ערכים מסוג Null ויצור מעין עיוות בנתונים שיתכן שלא התכוונו אליו.
אדגים בעזרת דוגמא פשוטה שנוצרת לעיתים בעקבות שימוש בLEFT JOIN.
SELECT Client_name, Revenue
FROM managed_clients left join client_revenues
ON managed_clients.client_id = client_revenue.client_id
Revenue | Client_name |
---|---|
100 | Vandalay Industries |
200 | The Human Fund |
Null | Dr. Martin Van Nostrand |
נניח ונרצה לברר את ממוצע ההכנסות של כלל החברות אותן אנחנו מנהלים. ככל הנראה נכתוב קווארי כזה:
SELECT AVG(Revenue)
FROM managed_clients left join client_revenues
ON managed_clients.client_id = client_revenue.client_id
AVG |
---|
150 |
מכיוון שDr. Martin Van Nostrand לא ביצע עסקאות, הוא קיבל את הערך NULL. ניתן לראות שחישוב הממוצע התבצע על שני הערכים שאינם Null וערך הNull סונן החוצה לפני ביצוע החישוב. כשSQL מבצע אגרגציה, הוא לא מתייחס לערך Null כ-0 למרות שיתכן שלזה התכוונו כשביקשנו למצוא ממוצע לכל חודש. אנחנו נרצה להכליל את הלקוח עם ערך הNull בחישוב הממוצע כי הוא לקוח מנוהל שחשוב לנו להתייחס אליו באנליזה.
מה עושים כדי לפתור את זה? משתמשים בפונקציית isnull. פונקציה שימושית מאוד שכופה על SQL את הלוגיקה הבאה: אם הערך הוא Null תהפוך אותו לX. במקרה שלנו, נרצה להפוך אותו ל0.
הפונקציה מקבלת שני פרמטרים, הראשון הוא העמודה שתרצו לבדוק אם היא מכילה ערך Null והשני הוא הערך שתרצו שיקבל במקום. זה יכול להיות 0 אבל יכול להיות גם כל ערך אחר כמו String או Boolean.
ניתן לראות כי כאן השתמשתי ב0 ועכשיו החישוב כלל גם את ההכנסה שלא התקבלה מDr. Martin Van Nostrand.
SELECT AVG(ISNULL(Revenue), 0)
FROM managed_clients left join client_revenues
ON managed_clients.client_id = client_revenue.client_id
AVG |
---|
83.3333 |
Take Home Message
בין אם מדובר בניואנסים של איך SQL עובד, ובין אם מדובר בניואנסים של הדאטה בייס עליו אתם עובדים, מאוד קל לטעות כשעובדים עם דאטה. הפתרון הטוב ביותר ביותר להימנעות מטעויות הוא לפתח מודעות. הפתרון הבא הכי טוב הוא לבדוק את עצמכם בכל שלב בבניית הקווארי, בין אם בדיקה ויזואלית של הנתונים בדאטה סט ובין אם השוואה לSource Of Truth אחר.
מודעות לNulls בדאטה סט שלכם ולדרך אליה מתייחס SQL קריטית אם רוצים להימנע מטעויות מביכות. ממליץ לכם לבדוק תמיד היכן נמצאים Nulls בדאטה סט ולחשוב האם אתם מתייחסים אליהם בזהירות הרצויה.
חושבים על טעויות נפוצות אחרות? אשמח לשמוע בתגובות.
סחטיין, העברתי לג׳וניורים אצלנו והם אהבו מאד!
אהבתי, מעביר לקבוצה