Menu

SQLite DROP TABLE ו-ALTER TABLE: הסבר על שינויי סכמה

איך מוחקים טבלאות, משנים את שמן ומשנים אותן ב-SQLite: מה ALTER TABLE תומך בו, במה לא, וטריק הבנייה מחדש לשינויים שהוא לא יכול לעשות.

בדף הזה יש עורכים שאפשר להריץ - לערוך, להריץ ולראות את הפלט מיד.

סכמות משתנות. SQLite מאפשרת לשנות אותן, ברוב המקרים.

ברגע שטבלה קיימת, בשלב כלשהו תרצו לשנות את השם שלה, להוסיף עמודה, להסיר עמודה או לבנות את כולה מחדש. SQLite תומכת במקרים הנפוצים ישירות עם DROP TABLE ו-ALTER TABLE, ונותנת פתרון מתועד לכל השאר.

הקאץ': ALTER TABLE של SQLite מוגבלת הרבה יותר מאשר ב-Postgres או ב-MySQL. לדעת מה היא יכולה ומה לא, ואת דפוס הבנייה מחדש לדברים שהיא לא יכולה, זה רוב המיומנות כאן.

DROP TABLE מסירה טבלה וכל מה שמחובר אליה

DROP TABLE מוחקת את הטבלה, את השורות שלה, את האינדקסים שלה וכל trigger שהוגדר עליה. אין ביטול:

הטבלה נעלמה. שאילתה עליה עכשיו תזרוק no such table: scratch.

אם אתם לא בטוחים אם הטבלה קיימת, מצב נפוץ בסקריפטים של הקמה, השתמשו ב-IF EXISTS כדי שהפקודה לא תעשה כלום בשקט כשהיא חסרה:

בלי IF EXISTS, המחיקה השנייה הייתה נכשלת בשגיאה. איתו, שתיהן רצות בלי בעיה.

מפתחות זרים יכולים לחסום DROP

אם אכיפת מפתחות זרים פועלת (PRAGMA foreign_keys = ON;) וטבלה אחרת מפנה לזו שאתם מוחקים, המחיקה נכשלת:

sqlite> PRAGMA foreign_keys = ON;
sqlite> DROP TABLE users;
Runtime error: FOREIGN KEY constraint failed

יש לכם כמה אפשרויות: למחוק קודם את הטבלה המפנה, למחוק את השורות המפנות, או להגדיר את המפתח הזר עם ON DELETE CASCADE כשיוצרים אותו. SQLite לא תשבור בשבילכם בשקט את השלמות ההפניות.

ALTER TABLE: ארבעת הדברים שהיא יכולה לעשות

ALTER TABLE של SQLite תומכת בדיוק בארבע פעולות:

כל אחת מהן רצה כפקודה יחידה. שתי הראשונות כמעט לא עולות כלום: הן רק מעדכנות את הסכמה. גם ADD COLUMN מהירה: SQLite לא כותבת את הטבלה מחדש, היא רק רושמת את הגדרת העמודה החדשה. DROP COLUMN כבדה יותר: SQLite צריכה לכתוב מחדש כל שורה כדי להסיר פיזית את הנתונים של העמודה.

ADD COLUMN עם ערך ברירת מחדל

עמודה חדשה בטבלה קיימת מתחילה כ-NULL בכל שורה, אלא אם נותנים לה ערך ברירת מחדל:

שתי השורות הקיימות מקבלות 'active'. ערך ברירת המחדל חייב להיות קבוע: SQLite לא תאפשר להשתמש ב-CURRENT_TIMESTAMP או בכל ביטוי לא קבוע אחר כברירת מחדל ב-ADD COLUMN, כי היא צריכה ערך שאפשר להחיל על כל שורה קיימת בלי לחשב אותו לכל שורה.

אם אתם צריכים NOT NULL בלי ערך ברירת מחדל, תצטרכו להוסיף את העמודה כך שתקבל NULL, למלא אותה עם UPDATE, ואז לבנות את הטבלה מחדש כדי להוסיף את האילוץ. וזה מביא אותנו למגבלות.

מה ALTER TABLE לא יכולה לעשות

דברים שעובדים ב-Postgres או ב-MySQL אבל לא ב-SQLite:

  • לשנות טיפוס של עמודה (ALTER COLUMN ... TYPE ...).
  • לשנות את ערך ברירת המחדל של עמודה במקום.
  • להוסיף או להסיר NOT NULL, CHECK, UNIQUE או PRIMARY KEY בעמודה קיימת.
  • להוסיף מפתח זר לעמודה קיימת.
  • לשנות את סדר העמודות.

ניסיון לעשות אחד מאלה נותן שגיאת תחביר. ל-SQLite אין בכלל פסוקית ALTER COLUMN. התשובה הרשמית זהה לכולם: לבנות את הטבלה מחדש.

דפוס הבנייה מחדש

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

עכשיו users.age הוא מספר שלם עם אילוץ check, ו-email הוא NOT NULL. הנתונים הגיעו יחד איתם.

כמה דברים שכדאי לזכור כשעושים את זה באמת:

  • כבו מפתחות זרים לכל משך הפעולה. אם טבלאות אחרות מפנות לשלכם, הריצו PRAGMA foreign_keys = OFF; לפני הטרנזקציה ו-PRAGMA foreign_keys = ON; אחריה. אחרת ה-DROP TABLE ייכשל. אי אפשר לשנות את ה-pragma בתוך טרנזקציה, אז הגדירו אותו מחוץ לה.
  • צרו מחדש אינדקסים ו-triggers. מחיקת הטבלה הישנה מוחקת גם את האינדקסים וה-triggers שלה. הוסיפו אותם שוב לטבלה החדשה אחרי שינוי השם.
  • בדקו views. views שמפנים לטבלה עדיין מצביעים על השם הישן ב-SQL השמור שלהם. בנו מחדש כל view שתלוי בעמודות שהשתנו.

דפוס הבנייה מחדש ארוך אבל אמין. זה מה שכלי מיגרציה כמו Alembic ו-Rails עושים מאחורי הקלעים כשהם עובדים מול SQLite.

מחיקת כמה טבלאות

אין פקודה יחידה למחיקת כמה טבלאות: מריצים DROP TABLE לכל אחת. בתוך טרנזקציה, אם רוצים שהן יקובצו יחד:

עטיפה בטרנזקציה אומרת שאו ששלוש המחיקות מצליחות או שאף אחת לא: שימושי כשמפרקים טבלאות קשורות שעלולות להיכשל באמצע בגלל מפתחות זרים.

מה לקחת מכאן

  • DROP TABLE מסירה טבלה ואת האינדקסים וה-triggers שלה. השתמשו ב-IF EXISTS לסקריפטים אידמפוטנטיים.
  • ALTER TABLE עושה רק ארבעה דברים: שינוי שם טבלה, שינוי שם עמודה, הוספת עמודה, מחיקת עמודה.
  • לכל השאר, שינויי טיפוס, אילוצים חדשים, מפתחות זרים על עמודות קיימות, בנו את הטבלה מחדש בתוך טרנזקציה.
  • שימו לב למפתחות זרים, לאינדקסים, ל-triggers ול-views כשבונים מחדש. הם לא עוברים עם הנתונים אוטומטית.

הבא בתור: הכנסת נתונים

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

שאלות נפוצות

איך מוחקים טבלה ב-SQLite?

משתמשים ב-DROP TABLE table_name;. הוסיפו IF EXISTS כדי שהפקודה לא תעשה כלום כשהטבלה לא קיימת: DROP TABLE IF EXISTS users;. מחיקת טבלה מסירה גם את האינדקסים וה-triggers שלה, ואם מפתחות זרים נאכפים, המחיקה נכשלת כשטבלאות אחרות עדיין מפנות אליה.

מה ALTER TABLE יכול לעשות ב-SQLite?

ארבעה דברים: RENAME TO (שינוי שם הטבלה), RENAME COLUMN ... TO ... (שינוי שם של עמודה), ADD COLUMN (הוספת עמודה חדשה בסוף) ו-DROP COLUMN (הסרת עמודה, מאז SQLite 3.35). זהו: אי אפשר לשנות טיפוס של עמודה, לשנות את ערך ברירת המחדל שלה במקום, או להוסיף אילוץ לעמודה קיימת.

איך משנים טיפוס או אילוצים של עמודה ב-SQLite?

SQLite לא תומכת בזה ישירות. הפתרון המקובל הוא דפוס הבנייה מחדש: יוצרים טבלה חדשה עם הסכמה הרצויה, INSERT INTO new SELECT ... FROM old, DROP TABLE old, ואז ALTER TABLE new RENAME TO old. עטפו את כל התהליך בטרנזקציה כדי שיהיה אטומי.

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל