עמודה מחושבת היא עמודה שהערך שלה מחושב
עמודה מחושבת (generated column) היא עמודה שהערך שלה מגיע מביטוי, ולא מ-INSERT. מצהירים על הנוסחה פעם אחת ב-CREATE TABLE, ו-SQLite דואגת לכל השאר. אף פעם לא כותבים אליה, וניסיון לעשות זאת הוא שגיאה.
הדוגמה הקצרה ביותר האפשרית:
total מעולם לא הוכנסה, אבל היא מופיעה בכל שורה. SQLite מחשבת אותה מחדש מ-price + tax בכל פעם שקוראים את השורה. עדכנו אחת מהעמודות, ו-total תתעדכן בהתאם.
צירוף המילים GENERATED ALWAYS AS הוא חובה. ה-ALWAYS הוא פורמליות של תקן SQL: אין ב-SQLite אפשרות אחרת.
VIRTUAL מול STORED
כל עמודה מחושבת היא מאחד משני סוגים. ברירת המחדל היא VIRTUAL:
המודל המנטלי:
VIRTUAL: עולה אפס בתים בדיסק, ועולה זמן מעבד בכל קריאה. זולה להוספה וזולה לשינוי בהמשך.STORED: עולה מקום בדיסק, ולא עולה שום דבר נוסף בקריאה. משתלמת כשהביטוי יקר או כשקוראים את העמודה הרבה יותר משכותבים אליה.
אם לא כותבים מילת מפתח, מקבלים VIRTUAL. זו כמעט תמיד ברירת המחדל הנכונה.
למה בכלל? ערכים נגזרים שאפשר לאנדקס
התכונה החזקה באמת היא שאפשר לשים אינדקס על עמודה מחושבת. כך מקבלים חיפוש מהיר לפי ערכים נגזרים בלי לשכתב כל שאילתה.
נניח שאתם רוצים חיפוש אימייל שלא רגיש לאותיות גדולות וקטנות:
האינדקס מכסה את הצורה באותיות קטנות. שאילתה שמסננת לפי email_lower משתמשת באינדקס ישירות. ל-SQLite יש אפילו אינדקסים על ביטויים (CREATE INDEX ... ON users(lower(email))), אבל עמודה מחושבת הופכת את הערך הנגזר לעמודה אמיתית שאפשר לעשות לה SELECT, להפנות אליה ב-views ולהשתמש בה שוב מקוד האפליקציה.
חילוץ ערכים מ-JSON
עמודות מחושבות מבריקות מעל JSON. התמיכה של SQLite ב-JSON נותנת את ->> לחילוץ ערך סקלרי; עטפו אותו בעמודה מחושבת וקיבלתם שדה עם טיפוס שאפשר לאנדקס, מעל blob גמיש.
user_id ו-kind נראות לשאילתות שלכם כמו עמודות רגילות, אבל הנתונים יושבים ב-payload. שנו את ה-JSON, והעמודות מתעדכנות. האינדקס על user_id הופך את החיפוש למהיר.
כללים ואילוצים
כמה דברים ש-SQLite אוכפת, וכדאי להכיר לפני שנתקלים בהם:
- הביטוי חייב להיות דטרמיניסטי.
random(),datetime('now')ופונקציות לא דטרמיניסטיות אחרות אסורות. הערך צריך להיות ניתן לשחזור מהשורה. - הביטוי יכול להפנות רק לעמודות באותה שורה. בלי תת-שאילתות, בלי צבירות, בלי טבלאות אחרות.
- אי אפשר לבצע
INSERTאוUPDATEישירות לעמודה מחושבת.INSERT INTO products (total) VALUES (5)היא שגיאה. - אי אפשר להוסיף עמודות
STOREDעםALTER TABLE ... ADD COLUMN. רקVIRTUALאפשר להוסיף בדיעבד. - לעמודות מחושבות יכולים להיות אילוצי
NOT NULL,CHECK,UNIQUEואפילוFOREIGN KEY. מבחינה זו הן מתנהגות כמו כל עמודה אחרת.
הדגמה מהירה של כלל הכתיבה:
sqlite> INSERT INTO products (price, tax, total) VALUES (10, 1, 999);
Runtime error: cannot INSERT into generated column "total"
הפתרון הוא להוציא את העמודה המחושבת מרשימת ה-INSERT ולתת ל-SQLite לחשב אותה.
לבחור VIRTUAL או STORED
ההחלטה מסתכמת בדרך כלל ביחס בין קריאות לכתיבות ובעלות הביטוי:
כללי אצבע:
- בחרו ב-
VIRTUALכברירת מחדל. היא חינמית בזמן הכתיבה ומתאימה כמעט לכל דבר. - עברו ל-
STOREDכשאתם מאנדקסים את העמודה בטבלה עם הרבה כתיבות (האינדקס צריך שהערך יישמר בכל מקרה), או כשהביטוי יקר באמת. - אל תתייסרו בהחלטה. הסוג הוא חלק מהסכמה, אבל אפשר למחוק וליצור מחדש את העמודה אם תשנו את דעתכם, לפחות עבור
VIRTUAL.
עמודות מחושבות מול Views
יש חפיפה עם views: שניהם חושפים ערכים מחושבים בלי לשמור אותם (טוב, לפעמים). החלוקה היא בדרך כלל:
- עמודה מחושבת שייכת לשורה אחת ולטבלה אחת. השתמשו בה לנגזרות ברמת השורה: עיצוב אימייל, חילוץ שדה JSON, חישוב סכום.
- View הוא שאילתה שמורה. השתמשו בו כשהחישוב כולל JOIN, צבירה או סינון על פני שורות.
אפשר לשלב ביניהם. View יכול לבצע SELECT מטבלה שיש בה עמודות מחושבות ולצרף הקשר נוסף. עמודות מחושבות יושבות בשכבת האחסון; views יושבים בשכבת השאילתות.
הצעד הבא: ATTACH DATABASE
עמודות מחושבות מאפשרות לטבלה אחת לחשב ערכים משלה. העמוד הבא הולך בכיוון ההפוך: חיבור של כמה מסדי נתונים של SQLite בבת אחת עם ATTACH DATABASE, כך ששאילתה אחת יכולה להשתרע על פני כמה קבצים.
שאלות נפוצות
מה זו עמודה מחושבת ב-SQLite?
עמודה מחושבת (generated column) היא עמודה שהערך שלה מחושב מביטוי שמשתמש בעמודות אחרות באותה שורה. מצהירים עליה עם GENERATED ALWAYS AS (expression) ב-CREATE TABLE. אף פעם לא כותבים אליה ישירות: SQLite מחשבת אותה בשבילכם בכל פעם שהשורה נקראת או נשמרת.
מה ההבדל בין עמודות מחושבות VIRTUAL ל-STORED?
עמודת VIRTUAL מחושבת בכל קריאה ולא תופסת מקום בדיסק, והיא ברירת המחדל. עמודת STORED מחושבת פעם אחת בזמן הכתיבה ונשמרת בקובץ מסד הנתונים, כך שהקריאות זולות יותר והכתיבות מעט יקרות יותר. אפשר לאנדקס את שתיהן, אבל STORED היא בדרך כלל הבחירה הנכונה כשהביטוי כבד או כשקוראים את העמודה הרבה יותר משכותבים אליה.
אפשר לאנדקס עמודה מחושבת ב-SQLite?
כן. CREATE INDEX עובד על עמודות מחושבות, גם VIRTUAL וגם STORED. זו הסיבה העיקרית להשתמש בהן: אפשר לאנדקס ערך נגזר (כמו lower(email) או שדה JSON שחולץ עם ->>) ולתת למתכנן השאילתות להשתמש באינדקס הזה בלי לשכתב כל שאילתה.
אפשר להוסיף עמודה מחושבת עם ALTER TABLE?
כן, אבל רק עמודות VIRTUAL. ALTER TABLE ... ADD COLUMN ... GENERATED ALWAYS AS (...) VIRTUAL עובד בלי בעיה. הוספת עמודה מחושבת מסוג STORED עם ALTER TABLE לא נתמכת, ותצטרכו לבנות את הטבלה מחדש. תכננו מראש אם אתם רוצים עמודות שמורות בטבלאות קיימות.