Affinity הוא העדפה, לא כלל
SQLite הוא בעל טיפוסיות דינמית. ערך נושא איתו את סוג האחסון שלו (NULL, INTEGER, REAL, TEXT, BLOB), והטיפוס שהוגדר לעמודה לא מגביל באופן קשיח מה אפשר להכניס אליה. מה שהטיפוס המוגדר כן עושה הוא לתת לעמודה affinity: סוג אחסון מועדף ש-SQLite מנסה להמיר אליו ערכים נכנסים.
שימו לב מה קורה כש-affinity לא מספיק כדי לעצור אי התאמה:
השורה השנייה שומרת את המחרוזת 'two' בעמודת INTEGER. SQLite ניסה להמיר את 'two' למספר, לא הצליח (זה לא מספרי), ושמר אותו בכל זאת כ-TEXT. typeof() חושף את סוג האחסון האמיתי של כל ערך, והוא לא תמיד מה שהגדרת העמודה מרמזת.
זה מפתיע אנשים שמגיעים מ-Postgres או מ-MySQL. זה בכוונה.
חמשת סוגי ה-Affinity
כל עמודה בטבלה שאינה STRICT מקבלת בדיוק אחד מאלה:
TEXT: מעדיף מחרוזות.NUMERIC: מעדיף מספרים, אבל מקבל טקסט אם אי אפשר להמיר.INTEGER: כמוNUMERIC, אבל שומר ערכים בלי חלק שברי כמספרים שלמים.REAL: מעדיף מספרים עשרוניים (נקודה צפה).BLOB: בלי העדפה, שומר כל מה שנותנים לו.
ל-affinity מסוג BLOB קוראים גם "ללא affinity": זה מה שמקבלים כשלא מגדירים טיפוס בכלל.
אותו קלט, המחרוזת '42', וחמישה טיפוסים שמורים שונים. כל עמודה המירה (או לא) לפי ה-affinity שלה.
איך SQLite בוחר Affinity מתוך ההגדרה שלכם
הנה החלק שמכשיל אנשים: ל-SQLite אין רשימה קבועה של טיפוסים "חוקיים". אפשר לכתוב כמעט כל דבר אחרי שם עמודה, ו-SQLite מסיק את ה-affinity על ידי סריקת הטקסט ומציאת תת מחרוזות, בסדר הזה:
- מכיל
INT→INTEGER - מכיל
CHAR,CLOBאוTEXT→TEXT - מכיל
BLOB, או שאין טיפוס בכלל →BLOB - מכיל
REAL,FLOAאוDOUB→REAL - כל דבר אחר →
NUMERIC
זה כל האלגוריתם. הוא מסביר הרבה מוזרויות:
FLOATING_POINTS הופך ל-INTEGER כי תת המחרוזת INT מופיעה ב-POINTS. הכלל הראשון שמתאים מנצח, מלמעלה למטה. זו הסיבה שהעתקה עיוורת של טיפוסים ממסד נתונים אחר יכולה לתת משהו שונה ממה שציפיתם.
Affinity בפעולה: המרות בזמן הכנסה
Affinity חשוב במיוחד כש-SQLite מחליט אם להמיר את הערך שלכם או לשמור אותו כמו שהוא. הכללים:
- affinity מסוג
TEXT: מספרים ו-BLOBים מומרים לטקסט. - affinity מסוג
NUMERIC,INTEGER,REAL: טקסט שנראה כמו מספר מומר. טקסט שלא נראה כך נשאר טקסט. - affinity מסוג
BLOB: שום דבר לא מומר.
שורה אחר שורה:
'123'בעמודתNUMERICהופך למספר השלם123. ההמרה מטקסט למספר הצליחה ולא איבדה מידע.'12.5'הופך למספר העשרוני12.5.'hello'ב-NUMERICנשאר טקסט: אין מספר להמיר אליו.- עמודת ה-
TEXTממירה מספרים לצורת המחרוזת שלהם. - עמודת ה-
BLOBשומרת הכול בדיוק כמו שניתן, כולל הטיפוס.
הקמט של INTEGER מול REAL
affinity מסוג INTEGER מתנהג כמעט זהה ל-NUMERIC, עם שינוי אחד: ערך כמו 3.0 שאין לו חלק שברי אמיתי נשמר כמספר השלם 3 כדי לחסוך מקום.
3.0 נוחת כ-INTEGER בשתי העמודות: האופטימיזציה קורית גם ב-NUMERIC. 3.5 שומר על החלק השברי ונשאר REAL. המסקנה: אל תסמכו על typeof() כדי לדעת אם עמודה הוגדרה INTEGER או REAL. הוא אומר מה באמת שמור, וזה יכול להשתנות משורה לשורה.
כש-Affinity נושך אתכם
הגמישות נוחה עד שהיא לא. שני סוגי כשלים מופיעים בקוד אמיתי:
1. נתונים גרועים מתגנבים פנימה. אם יש באפליקציה שלכם באג ששולח 'N/A' לעמודת INTEGER, SQLite ישמור אותו. שאילתות מאוחרות יותר שעושות חשבון על העמודה יחזירו תוצאות מוזרות או NULL. בלי שגיאה, בלי אזהרה, רק השחתה שקטה.
2. השוואות נהיות מוזרות. מיון ובדיקות שוויון מתייחסים אחרת לערכים מסוגי אחסון שונים:
מספרים שלמים ממוינים מספרית, ואז ערכי טקסט ממוינים לקסיקוגרפית, והם ממוינים אחרי כל המספרים. אז מקבלים 2, 3, 10 (המספרים השלמים בסדר מספרי), ואז '20', '100' (המחרוזות בסדר אלפביתי). לא מה שרוב האנשים רוצים.
אם אתם שולטים בהכנסות ומאמתים בקפידה, טבלאות רגילות הן בסדר. אם לא, או שאתם פשוט רוצים שמסד הנתונים יאכוף טיפוסים בשבילכם, יש אפשרות טובה יותר.
הבא בתור: טבלאות STRICT
SQLite 3.37 הוסיף טבלאות STRICT, שמכבות את ה-affinity ודוחות ערכים שלא תואמים לטיפוס המוגדר. הן נותנות את הטיפוסיות הדינמית כברירת מחדל כשאתם רוצים אותה, ואכיפה בסגנון Postgres כשלא. זה הדף הבא.
שאלות נפוצות
מה זה type affinity ב-SQLite?
Type affinity הוא סוג האחסון המועדף של עמודה. ל-SQLite יש חמישה: TEXT, NUMERIC, INTEGER, REAL ו-BLOB. כשמכניסים ערך, SQLite מנסה להמיר אותו ל-affinity של העמודה, אבל אם ההמרה תאבד מידע או בלתי אפשרית, הוא שומר את הערך כמו שהוא. Affinity הוא רמז, לא אילוץ קשיח.
איך SQLite מחליט מה ה-affinity של עמודה?
SQLite סורק את שם הטיפוס שכתבתם ב-CREATE TABLE ומחפש בו תת מחרוזות, לפי הסדר: אם הוא מכיל INT הוא INTEGER. אחרת, CHAR, CLOB או TEXT הופכים אותו ל-TEXT. אחרת, BLOB (או היעדר טיפוס) הופך אותו ל-BLOB. אחרת, REAL, FLOA או DOUB הופכים אותו ל-REAL. בכל מקרה אחר, NUMERIC. זו הסיבה ש-VARCHAR(50) הופך ל-TEXT ו-BIGINT הופך ל-INTEGER: המילים שאתם כותבים עוברות התאמת תבניות.
האם עמודה ב-SQLite יכולה להכיל ערכים מהטיפוס הלא נכון?
כן, בטבלאות רגילות. עמודה שהוגדרה INTEGER תשמור בשמחה את המחרוזת 'hello', כי affinity רק מציע המרה. אם אתם רוצים אכיפה קשיחה של טיפוסים, השתמשו בטבלאות STRICT, שדוחות ערכים לא תואמים על הסף. נכסה אותן בדף הבא.