הצטרפו לקבוצות שלנו לקבלת עדכונים מרוכזים פעם בשבוע:

ווטסאפ:
http://wa.dwh.co.il
טלגרם:
http://telegram.dwh.co.il

למען אלה שלא מכירים את נושא הconstraints נסביר נושא זה. Constraints הם סוגי הגבלות שנאכפים על הנתונים בDB ומטרתם היא ליצור מצב שבו הנתונים יהיו בעלי סדר והגיון ולא "סתם" אוסף של נתונים. מאמר זה דן בשאלה האם דין Constraints במחסני נתונים זהה לדינם במערכות תפעוליות.

כדוגמאות לconstraints אפשר להביא את Not Null האוכף שבשדה מסויים יהיה ערך, Primary\Unique  המונע רשומות כפולות בטבלה, לדוגמא איסור שבטבלת סוכנים יהיו שני מספרי סוכן זהים. כמו כן, ישנו constraint מסוג Foreign Key האוכף ששדה מסויים בטבלה, יהיה חייב להיות ערך בשדה אחר בטבלה אחרת.לדוגמא, מספר סוכן בטבלת מכירות יהיה חייב להיות מספר סוכן שקיים בטבלת סוכנים.

 

הטענה המכרזית שאנו טוענים היא שאומנם constraints חיונים בבסיסי נתונים תפעולים, אך כשמדובר במחסני נתונים אפשר ורצוי לוותר עליהם. טענה זאת יכולה להשמע מבלבלת, הרי לא צריך לשמור על הגיון וסדר כשמדובר במחסני נתונים? וודאי שכן. אבל ישנו הבדל אחד מהותי לצורך עניין זה , בין מערכות תפעוליות לבין מחסני נתונים-במערכות תפעוליות הנתונים מוכנסים לבסיס הנתונים On Line להבדיל ממחסני נתונים שבהם הנתונים מוכנסים בBatch בתהליך הנקרא גזירה וטעינה. הבדל זה מביא אותנו למחשבה שכפי שהנתונים מוכנסים בBatch, אפשר ורצוי לבדוק את שלמות הDB בBatch ללא הגדרת constraints בבסיס הנתונים.

 

מדוע לא להשתמש בדרך המסורתית של הגדרת constraints שבעזרתה, נדע תמיד כי הנתונים מאורגנים? ישנם לכך מספר סיבות:

-הפרת  constraints תגרום לשגיאה , ובמקרה של תהליכי גזירת נתונים , להכשלת תהליכי גזירה. לדוגמא, הכנסת ערך NULL לשדה עם constraint Not Null תגרום להודעת שגיאה. לעתים, כלי הגזירה השונים יכולים לאתר שגיאות אלו ולפעול בהתאם, אך עדיין הגדרת constraints יכולה להביא להכשלת תהליכים.

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

-לעתים constraints יכולים ליצור באופן אוטומטי אינדקסים לא לצורך.

 

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

נוכל לתאר זה בדיאגרמה הבאה:

constraints diagram

בואו נתייחס באופן פרטני לסוגי הconstraints.

 

Foreign Key

 

להזכירכם, המטרה בconstraint זה לגרום לכך שערך בשדה מטבלה מסויימת יהיה תמיד משדה בטבלה אחרת. הקשר הזה משמש בין טבלת הFact לטבלת הDim.

הדרך המהירה והטובה ביותר לבדוק זאת, היא באמצעות תחביר הexists. לדוגמא:

 

Select *  from fact

Where not exists (select 1 from Dim where Dim.key=Fact.key)

 

משפט זה יתן לנו את כל הערכים בטבלת ה Fact אשר אין להם ערך מקביל בטבלת הDim בחיבור באמצעות שדה key.

כמובן שמצב זה אינו תקין משום שכל שימוש בטבלת ה Fact יחד עם טבלת ה Dim , יגרום להשמטת ערכים מהתוצאה וזאת בהנחה שלא משתמשים בouter join.

הדרך לתקן מצב הוא על ידי הוספת ערכים "לא ידועים" לטבלת הDIM.

בדוגמא שלפנינו:

Insert into dim

Select distinct key, 'unknown'   from fact

Where not exists (select 1 from Dim where Dim.key=Fact.key)

הסבר: אנחנו יוצאים מנקודת הנחה שטבלת המימד מכילה שני שדות: הערך המקשר לטבלת הFact וכן ערך פענוח. אנחנו מכניסים לטבלת ה Dim ערכים יחודים של הערכים בFact שאין להם מקביל בDim וכמובן הפענוח של ערכים אלו יהיה "לא ידוע"- "Unknown".

 

Unique\Prime Key

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

הבדיקה היא פשוטה:

 (*)Select key, count

From table

Group by key

Having count(*)>1

הסבר:

לכל מפתח אנו סופרים את כמות הרשומות שלו, כמובן שאמורה להיות רשומה אחת בלבד. אנו נמצא את אלה הבעייתים, כלומר את אלה עם יותר משורה אחת. (משפט הHaving  האחרון).

 

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

לשמחתנו ישנו בבסיסי הנתונים מנגנון שמבדיל רשומות גם אם הם זהות. מנגנון זה הוא rownum\rowid הנתון לכל רשומה ערך יחודי, המשמש כמאין נומרטור.

Delete from table

Where rowid <> (select max (rowid)

                            From table_alise

                            Where table. Key= table_alise.key )

 

Not Null

 

Constraint זה שונה משאר הConstraints בכך שהוא מתייחס לשדה אחד ולרשומה אחת בכל פעם וכן בכך שבכל טבלה עלולים להיות מוגדרים constraints רבים מסוג זה. עקב, הטיפול הוא מעט שונה. בנסיבות רגילות, היינו יכולים לומר כי הבדיקה היא באמצעות isnull והתיקון באמצעות update של השדה בטבלה, אך ידע אמפירי מלמדנו כי אין זה פרקטי לבצע זאת על מספר רב של שדות. במקום זאת, אנו מציעים כי בכל טעינה של שדה שאנו רוצים למנוע NULL יש להשתמש בפונקציה מחסלת NULL, כגון NVL או Coalesce.

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

 

אופן העבודה במחסן הנתונים

 

השיטות שהראנו עד כה יפות ונכונות, אך האם אנו צריכים לבצע את כל הפעולות הללו כתחליף לכל constraint? לכאורה כן, אך ישנה שיטה טובה יותר: שימוש  ב Stored Procedures המקבל ערכים גנרים והמבצעת את הבדיקות ומתקנת את השגיאות. כמובן ש Stored Procedures אלו יכתבו ב PL\SQL או ב T-SQL בהתאם לסוג הDB. לצערנו, אין לנו דוגמאות קוד כאלה, אך ידוע לנו כי משתמשים בפרוצדורת כאלו בלא מעט פרוייקטים. אם מישהו רוצה לנדב לנו קוד שכזה נשמח מאד, ונפרסם בצירוף שם המחבר.

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

עוד מספר מילים

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

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