מה מפתח ראשי עושה בפועל
מפתח ראשי הוא העמודה (או צירוף העמודות) שמזהה באופן ייחודי כל שורה בטבלה. לשתי שורות אסור שיהיה אותו ערך מפתח ראשי. SQLite אוכפת את זה בשבילכם, ומשתמשת במפתח כדי למצוא שורות מהר.
הצורה הפשוטה ביותר נכתבת ישירות על עמודה:
לא סיפקתם id, ו-SQLite מילאה אחד. זה לא קסם: זה מקרה מיוחד של INTEGER PRIMARY KEY שכדאי להבין לפני שכותבים משהו אחר.
INTEGER PRIMARY KEY הוא מיוחד
ברוב מסדי הנתונים, מפתח ראשי הוא פשוט אינדקס ייחודי. ב-SQLite, לכל טבלה רגילה כבר יש מספר שלם נסתר של 64 ביט שנקרא rowid ומזהה שורות מבפנים. כשמצהירים על עמודה בדיוק כ-INTEGER PRIMARY KEY, העמודה הזו הופכת ל-rowid. בלי אינדקס נוסף, בלי אחסון נוסף: המזהה שלכם והמיקום הפיזי של השורה הם אותו דבר.
id ו-rowid הם אותה עמודה בשני שמות. חיפושים לפי id הולכים ישר לשורה, אין עץ שני לעבור בו. זו הסיבה שהעצה הסטנדרטית ל-SQLite היא: אם אתם רוצים מפתח ראשי מספרי, כתבו בדיוק INTEGER PRIMARY KEY. לא INT, לא BIGINT, לא INTEGER NOT NULL PRIMARY KEY (טוב, זה עובד, אבל הטיפוס חייב להיות INTEGER).
טיפוסים אחרים עדיין עובדים: הם פשוט מקבלים אינדקס ייחודי נפרד, וזה בסדר, רק לא קומפקטי באותה מידה.
בדרך כלל אין צורך ב-AUTOINCREMENT
רפלקס נפוץ ממסדי נתונים אחרים הוא לכתוב id INTEGER PRIMARY KEY AUTOINCREMENT. ב-SQLite, מילת המפתח AUTOINCREMENT עושה משהו צר יותר ממה שהשם מרמז, וברוב המקרים לא צריך אותה.
בלי AUTOINCREMENT, עמודת INTEGER PRIMARY KEY מתמלאת אוטומטית במספר שגדול באחד מה-rowid הגדול ביותר הקיים. אם מוחקים את השורה האחרונה, ההכנסה הבאה עשויה להשתמש שוב באותו מזהה.
עם AUTOINCREMENT, SQLite עוקבת אחרי המזהה הגבוה ביותר שאי פעם היה בשימוש בטבלה צדדית בשם sqlite_sequence ולעולם לא משתמשת שוב בערכים, גם אחרי מחיקה.
הטבלה plain השתמשה שוב במזהה 3. הטבלה עם AUTOINCREMENT קפצה ל-4. אלא אם יש לכם סיבה אמיתית לאסור שימוש חוזר במזהים, כמו ביקורת או הפניות חיצוניות שנשארות אחרי מחיקה, ותרו על AUTOINCREMENT. הוא עולה בכתיבה נוספת בכל הכנסה ובטבלת רישום נפרדת.
מפתחות ראשיים מורכבים
לפעמים עמודה אחת לא מספיקה. טבלת קישור שממפה משתמשים לתפקידים, למשל, מזוהה באופן ייחודי על ידי הזוג (user_id, role_id). במקרה כזה מצהירים על המפתח ברמת הטבלה:
הזוג חייב להיות ייחודי בכל הטבלה: (1, 10) יכול להופיע רק פעם אחת. כל עמודה לבדה יכולה לחזור בחופשיות. זו בדיוק הנקודה: לכל משתמש יכולים להיות תפקידים רבים, לכל תפקיד יכולים להיות משתמשים רבים, אבל צירוף מסוים של משתמש ותפקיד קיים לכל היותר פעם אחת.
מפתח ראשי מורכב יוצר אינדקס נפרד שמכסה את העמודות הרשומות. הוא לא הופך ל-rowid: רק INTEGER PRIMARY KEY בודד מקבל את היחס הזה.
המלכודת של NULL במפתח ראשי
הנה מוזרות שמפתיעה אנשים שמגיעים מ-PostgreSQL או מ-MySQL: בטבלת SQLite רגילה, עמודת מפתח ראשי שאינה INTEGER PRIMARY KEY יכולה להכיל NULL. זה באג ותיק שמחברי SQLite השאירו לטובת תאימות לאחור.
שתי שורות עם NULL חמקו מהמפתח הראשי. הפתרון הוא להוסיף NOT NULL במפורש על כל עמודת מפתח ראשי שאינה מספר שלם:
או להשתמש בטבלת STRICT, שבה הבאג של NULL במפתח ראשי מתוקן. ההרגל לכתוב NOT NULL על כל עמודת מפתח ראשי הוא ביטוח זול.
מפתח ראשי מול UNIQUE
שניהם מונעים כפילויות. ההבדלים:
- לטבלה יש לכל היותר מפתח ראשי אחד, אבל יכולים להיות לה אילוצי
UNIQUEרבים. - המפתח הראשי הוא המזהה "הראשי" של הטבלה: מפתחות זרים מצביעים עליו כברירת מחדל.
INTEGER PRIMARY KEYהופך ל-rowid, ועמודה מספרית עםUNIQUEלא.- עמודות
UNIQUEמקבלות בשמחה כמהNULL(כל NULL נחשב שונה).
id הוא הזהות של השורה. email ו-username גם הם ייחודיים, אבל הם מאפיינים עסקיים: הם יכולים להשתנות, ואילו ה-id לא אמור להשתנות.
הוספת מפתח ראשי מאוחר יותר (בעיקר: אל תעשו את זה)
ה-ALTER TABLE של SQLite מוגבל. אי אפשר להריץ ALTER TABLE ... ADD PRIMARY KEY: הפקודה הזו לא קיימת. אם שכחתם את המפתח הראשי ובטבלה כבר יש נתונים, הדרך היא ליצור אותה מחדש:
זה ריקוד המיגרציה הסטנדרטי של SQLite. בקוד אמיתי עטפו אותו בטרנזקציה, וכבו לרגע את המפתחות הזרים אם טבלאות אחרות מפנות לטבלה הזו. הלקח: קבעו את המפתח הראשי נכון כבר ב-CREATE TABLE.
רשימת בדיקה מהירה
כשאתם כותבים טבלה חדשה, שאלו:
- האם לשורה יש מזהה ייחודי טבעי? אם זה מספר שלם יחיד, השתמשו ב-
INTEGER PRIMARY KEY. - האם הזהות היא בעצם צירוף של עמודות (טבלת קישור)? השתמשו ב-
PRIMARY KEY (col_a, col_b)ברמת הטבלה. - האם המפתח הוא טקסט או טיפוס אחר שאינו מספר שלם? הוסיפו
NOT NULLבמפורש. - האם אתם באמת צריכים
AUTOINCREMENT? כנראה שלא. - האם הטבלה קטנה, בעיקר לקריאה, ועם מפתח ראשי שאינו מספר שלם? שקלו
WITHOUT ROWID(מוסבר בעמוד על rowid).
הבא: rowid
INTEGER PRIMARY KEY הופיע לרגע בתור "כינוי ל-rowid", אבל rowid הוא הבסיס שמתחת לכל טבלת SQLite רגילה, וכדאי להבין אותו ישירות. זה העמוד הבא.
שאלות נפוצות
איך מגדירים מפתח ראשי ב-SQLite?
הוסיפו PRIMARY KEY לעמודה בפקודת ה-CREATE TABLE, למשל id INTEGER PRIMARY KEY. למפתח שמשתרע על כמה עמודות, השתמשו באילוץ ברמת הטבלה: PRIMARY KEY (col_a, col_b). העמודה או הצירוף חייבים להיות ייחודיים בכל השורות.
מה ההבדל בין INTEGER PRIMARY KEY למפתחות ראשיים אחרים ב-SQLite?
INTEGER PRIMARY KEY מיוחד: הוא הופך לכינוי של ה-rowid המובנה של הטבלה, ולכן הוא נשמר ישירות ב-B-tree בלי אינדקס נוסף. כל טיפוס אחר, או מפתח מורכב, מקבל אינדקס ייחודי נפרד. למזהים מספריים בעמודה אחת, INTEGER PRIMARY KEY מהיר וקטן יותר.
האם צריך AUTOINCREMENT על מפתח ראשי ב-SQLite?
בדרך כלל לא. INTEGER PRIMARY KEY כבר מקצה אוטומטית rowid ייחודי כשמכניסים NULL. AUTOINCREMENT רק מוסיף הבטחה שמזהים לעולם לא ישמשו שוב אחרי מחיקה, במחיר של טבלת sqlite_sequence נוספת. ותרו עליו אלא אם אתם צריכים במפורש מזהים שרק עולים.
למה המפתח הראשי שלי ב-SQLite מאפשר ערכי NULL?
באג היסטורי שנשמר לטובת תאימות: בטבלאות רגילות, עמודת מפתח ראשי שאינה INTEGER יכולה לקבל NULL אלא אם מוסיפים במפורש NOT NULL. עמודת INTEGER PRIMARY KEY היא החריגה: היא אף פעם לא מאפשרת NULL. ליתר ביטחון, כתבו NOT NULL על כל עמודת מפתח ראשי, או השתמשו בטבלת STRICT, שבה הכלל נאכף כמו שצריך.