Menu

DROP TABLE i ALTER TABLE w SQLite: zmiany schematu w praktyce

Jak usuwać tabele, zmieniać ich nazwy i modyfikować je w SQLite: co obsługuje ALTER TABLE, czego nie obsługuje i jak przebudować tabelę, gdy ALTER nie wystarcza.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Schematy się zmieniają, a SQLite pozwala je zmieniać (w większości)

Gdy tabela już istnieje, prędzej czy później zechcesz zmienić jej nazwę, dodać kolumnę, usunąć kolumnę albo przebudować całość. SQLite obsługuje typowe przypadki bezpośrednio przez DROP TABLE i ALTER TABLE, a na całą resztę daje udokumentowane obejście.

Haczyk: ALTER TABLE w SQLite jest dużo bardziej ograniczone niż w Postgres czy MySQL. Wiedza o tym, co potrafi, a czego nie, oraz znajomość wzorca przebudowy dla tego, czego nie potrafi, to większość umiejętności w tym temacie.

DROP TABLE usuwa tabelę i wszystko, co z nią związane

DROP TABLE usuwa tabelę, jej wiersze, indeksy i wszystkie zdefiniowane na niej triggery. Nie ma cofania:

Tabeli już nie ma. Zapytanie do niej zgłosiłoby teraz no such table: scratch.

Jeśli nie masz pewności, czy tabela istnieje (częste w skryptach konfiguracyjnych), użyj IF EXISTS, aby instrukcja po cichu nic nie robiła, gdy tabeli brakuje:

Bez IF EXISTS drugie usunięcie zgłosiłoby błąd. Z nim oba wykonują się bez problemu.

Klucze obce mogą zablokować DROP

Jeśli egzekwowanie kluczy obcych jest włączone (PRAGMA foreign_keys = ON;), a inna tabela odwołuje się do tej, którą usuwasz, usunięcie się nie uda:

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

Masz kilka opcji: najpierw usuń tabelę, która się odwołuje, usuń odwołujące się wiersze albo przy tworzeniu zdefiniuj klucz obcy z ON DELETE CASCADE. SQLite nie złamie za ciebie po cichu integralności referencyjnej.

ALTER TABLE: cztery rzeczy, które potrafi

ALTER TABLE w SQLite obsługuje dokładnie cztery operacje:

Każda z nich to jedna instrukcja. Pierwsze dwie są praktycznie darmowe, bo tylko aktualizują schemat. ADD COLUMN też jest szybkie: SQLite nie przepisuje tabeli, tylko zapisuje definicję nowej kolumny. DROP COLUMN jest cięższe, bo SQLite musi przepisać każdy wiersz, aby fizycznie usunąć dane kolumny.

ADD COLUMN z wartością domyślną

Nowa kolumna w istniejącej tabeli ma na starcie NULL w każdym wierszu, chyba że nadasz jej wartość domyślną:

Oba istniejące wiersze dostają wartość 'active'. Wartość domyślna musi być stałą: SQLite nie pozwoli użyć CURRENT_TIMESTAMP ani żadnego innego niestałego wyrażenia jako wartości domyślnej w ADD COLUMN, bo potrzebuje wartości, którą może zastosować do każdego istniejącego wiersza bez obliczania jej osobno dla każdego.

Jeśli potrzebujesz NOT NULL bez wartości domyślnej, musisz dodać kolumnę dopuszczającą NULL, uzupełnić ją przez UPDATE, a potem przebudować tabelę, aby dodać ograniczenie. I tu dochodzimy do ograniczeń.

Czego ALTER TABLE nie potrafi

Rzeczy, które działają w Postgres czy MySQL, ale nie w SQLite:

  • Zmiana typu kolumny (ALTER COLUMN ... TYPE ...).
  • Zmiana wartości domyślnej kolumny w miejscu.
  • Dodanie lub usunięcie NOT NULL, CHECK, UNIQUE lub PRIMARY KEY na istniejącej kolumnie.
  • Dodanie klucza obcego do istniejącej kolumny.
  • Zmiana kolejności kolumn.

Każda z tych prób kończy się błędem składni. SQLite w ogóle nie ma klauzuli ALTER COLUMN. Oficjalna odpowiedź jest dla wszystkich taka sama: przebuduj tabelę.

Wzorzec przebudowy

Gdy ALTER TABLE nie potrafi zrobić tego, czego potrzebujesz, tworzysz nową tabelę z docelowym schematem, kopiujesz dane, usuwasz starą tabelę i zmieniasz nazwę nowej na starą. Opakuj to w transakcję, aby wszystko wykonało się w całości albo wcale:

Teraz users.age to liczba całkowita z ograniczeniem CHECK, a email jest NOT NULL. Dane przeszły razem ze schematem.

Kilka rzeczy, o których warto pamiętać, robiąc to naprawdę:

  • Na czas operacji wyłącz klucze obce. Jeśli inne tabele odwołują się do twojej, uruchom PRAGMA foreign_keys = OFF; przed transakcją i PRAGMA foreign_keys = ON; po niej. W przeciwnym razie DROP TABLE się nie uda. Tej pragmy nie da się zmienić wewnątrz transakcji, więc ustaw ją na zewnątrz.
  • Odtwórz indeksy i triggery. Usunięcie starej tabeli usuwa też jej indeksy i triggery. Po zmianie nazwy dodaj je z powrotem do nowej tabeli.
  • Sprawdź widoki. Widoki odwołujące się do tabeli wciąż wskazują starą nazwę w zapisanym SQL. Przebuduj te, które zależą od zmienionych kolumn.

Wzorzec przebudowy jest rozwlekły, ale niezawodny. Tak właśnie pod spodem działają narzędzia migracji, takie jak Alembic czy Rails, gdy pracują z SQLite.

Usuwanie kilku tabel

Nie ma jednej instrukcji do usuwania wielu tabel: uruchamiasz DROP TABLE dla każdej z nich. Jeśli chcesz je zgrupować, zrób to w transakcji:

Opakowanie ich w transakcję oznacza, że albo wszystkie trzy usunięcia się udadzą, albo żadne. Przydaje się to przy usuwaniu powiązanych tabel, gdy w połowie coś może się nie udać z powodu kluczy obcych.

Co warto zapamiętać

  • DROP TABLE usuwa tabelę razem z jej indeksami i triggerami. W skryptach idempotentnych używaj IF EXISTS.
  • ALTER TABLE robi tylko cztery rzeczy: zmienia nazwę tabeli, zmienia nazwę kolumny, dodaje kolumnę i usuwa kolumnę.
  • Wszystko inne (zmiany typów, nowe ograniczenia, klucze obce na istniejących kolumnach) wymaga przebudowy tabeli w transakcji.
  • Przy przebudowie pamiętaj o kluczach obcych, indeksach, triggerach i widokach. Nie podążają automatycznie za danymi.

Dalej: wstawianie danych

Za tobą rozdział o tabelach i ograniczeniach, które nadają im kształt. Czas je wypełnić: następny rozdział zaczyna się od INSERT, w tym formy wielowierszowej, wartości domyślnych i tego, jak SQLite obsługuje wstawienia sprzeczne z ograniczeniami.

Najczęściej zadawane pytania

Jak usunąć tabelę w SQLite?

Użyj DROP TABLE table_name;. Dodaj IF EXISTS, aby instrukcja nic nie robiła, gdy tabeli nie ma: DROP TABLE IF EXISTS users;. Usunięcie tabeli usuwa też jej indeksy i triggery, a jeśli klucze obce są egzekwowane, usunięcie się nie uda, gdy inne tabele wciąż się do niej odwołują.

Co potrafi ALTER TABLE w SQLite?

Cztery rzeczy: RENAME TO (zmiana nazwy tabeli), RENAME COLUMN ... TO ... (zmiana nazwy kolumny), ADD COLUMN (dodanie nowej kolumny na końcu) i DROP COLUMN (usunięcie kolumny, od SQLite 3.35). To wszystko: nie zmienisz typu kolumny, nie zmienisz w miejscu jej wartości domyślnej ani nie dodasz ograniczenia do istniejącej kolumny.

Jak zmienić typ lub ograniczenia kolumny w SQLite?

SQLite nie obsługuje tego bezpośrednio. Standardowe obejście to wzorzec przebudowy: utwórz nową tabelę z docelowym schematem, wykonaj INSERT INTO new SELECT ... FROM old, potem DROP TABLE old, a na końcu ALTER TABLE new RENAME TO old. Opakuj całość w transakcję, aby operacja była atomowa.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ