Menu

Kolumny generowane w SQLite: VIRTUAL a STORED z przykładami

Jak działają kolumny generowane w SQLite: deklarowanie, wybór między VIRTUAL a STORED i indeksowanie ich dla szybkiego wyszukiwania.

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

Kolumna generowana to kolumna wyliczana

Kolumna generowana to kolumna, której wartość pochodzi z wyrażenia, a nie z INSERT. Wzór deklarujesz raz w CREATE TABLE, a resztą zajmuje się SQLite. Nigdy do niej nie zapisujesz: próba zapisu kończy się błędem.

Najkrótszy możliwy przykład:

total nigdy nie zostało wstawione, a mimo to pojawia się w każdym wierszu. SQLite wylicza je na nowo z price + tax przy każdym odczycie wiersza. Zmień którąkolwiek z tych kolumn, a total się dostosuje.

Fraza GENERATED ALWAYS AS jest wymagana. ALWAYS to formalność ze standardu SQL: w SQLite nie ma innej opcji.

VIRTUAL a STORED

Każda kolumna generowana występuje w jednym z dwóch wariantów. Domyślny to VIRTUAL:

Model myślowy:

  • VIRTUAL: zero bajtów na dysku, koszt procesora przy każdym odczycie. Tania w dodaniu, tania w późniejszej zmianie.
  • STORED: zajmuje miejsce na dysku, odczyt nic dodatkowo nie kosztuje. Opłaca się, gdy wyrażenie jest kosztowne albo kolumna jest znacznie częściej czytana niż zapisywana.

Jeśli nie podasz słowa kluczowego, dostaniesz VIRTUAL. To prawie zawsze właściwy wybór domyślny.

Po co to wszystko? Indeksowalne wartości pochodne

Najmocniejsza zaleta to możliwość założenia indeksu na kolumnie generowanej. Dzięki temu masz szybkie wyszukiwanie po wartościach pochodnych bez przepisywania każdego zapytania.

Załóżmy, że chcesz wyszukiwać e-maile bez rozróżniania wielkości liter:

Indeks obejmuje wersję zapisaną małymi literami. Zapytanie filtrujące po email_lower korzysta z indeksu bezpośrednio. SQLite ma też indeksy na wyrażeniach (CREATE INDEX ... ON users(lower(email))), ale kolumna generowana sprawia, że wartość pochodna jest widoczna jako prawdziwa kolumna, którą możesz wybrać w SELECT, użyć w widokach i wykorzystać w kodzie aplikacji.

Wyciąganie wartości z JSON

Kolumny generowane świetnie sprawdzają się w połączeniu z JSON. Obsługa JSON w SQLite daje operator ->> do wyciągania wartości skalarnej. Opakuj go w kolumnę generowaną, a dostaniesz typowane, indeksowalne pole nad elastycznym obiektem.

user_id i kind wyglądają dla zapytań jak zwykłe kolumny, ale dane są w payload. Zmień JSON, a kolumny się zaktualizują. Indeks na user_id sprawia, że wyszukiwanie jest szybkie.

Zasady i ograniczenia

Kilka rzeczy, które SQLite wymusza. Warto je znać, zanim na nie trafisz:

  • Wyrażenie musi być deterministyczne. random(), datetime('now') i inne niedeterministyczne funkcje są niedozwolone. Wartość musi dać się odtworzyć z wiersza.
  • Wyrażenie może odwoływać się tylko do kolumn tego samego wiersza. Bez podzapytań, agregatów i innych tabel.
  • Nie możesz bezpośrednio wykonać INSERT ani UPDATE na kolumnie generowanej. INSERT INTO products (total) VALUES (5) to błąd.
  • Kolumn STORED nie można dodać przez ALTER TABLE ... ADD COLUMN. Po fakcie można dodać tylko VIRTUAL.
  • Kolumny generowane mogą mieć ograniczenia NOT NULL, CHECK, UNIQUE, a nawet FOREIGN KEY. Pod tym względem zachowują się jak każda inna kolumna.

Krótka demonstracja zasady zapisu:

sqlite> INSERT INTO products (price, tax, total) VALUES (10, 1, 999);
Runtime error: cannot INSERT into generated column "total"

Rozwiązanie: usuń kolumnę generowaną z listy w INSERT i pozwól SQLite ją wyliczyć.

Wybór między VIRTUAL a STORED

Decyzja zwykle sprowadza się do proporcji odczytów do zapisów i kosztu wyrażenia:

Praktyczne zasady:

  • Domyślnie wybieraj VIRTUAL. Nic nie kosztuje przy zapisie i nadaje się prawie do wszystkiego.
  • Przejdź na STORED, gdy indeksujesz kolumnę w tabeli z dużą liczbą zapisów (indeks i tak potrzebuje zapisanej wartości) albo gdy wyrażenie jest naprawdę kosztowne.
  • Nie zadręczaj się tym. Wariant jest częścią schematu, ale jeśli zmienisz zdanie, możesz usunąć kolumnę i utworzyć ją na nowo, przynajmniej w przypadku VIRTUAL.

Kolumny generowane a widoki

Kolumny generowane częściowo pokrywają się z widokami: jedne i drugie udostępniają wyliczone wartości bez ich przechowywania (no, czasami). Zwykle podział wygląda tak:

  • Kolumna generowana należy do jednego wiersza i jednej tabeli. Używaj jej do wartości pochodnych w obrębie wiersza: formatowania e-maila, wyciągania pola JSON, liczenia sumy.
  • Widok to zapisane zapytanie. Używaj go, gdy obliczenia wymagają złączeń, agregacji albo filtrowania wielu wierszy.

Możesz je łączyć. Widok może wybierać dane z tabeli z kolumnami generowanymi i dołączać dodatkowy kontekst. Kolumny generowane działają na warstwie przechowywania, a widoki na warstwie zapytań.

Dalej: ATTACH DATABASE

Kolumny generowane pozwalają jednej tabeli wyliczać własne wartości. Następna strona idzie w przeciwną stronę: łączy kilka baz SQLite naraz przez ATTACH DATABASE, żeby jedno zapytanie mogło obejmować wiele plików.

Najczęściej zadawane pytania

Czym jest kolumna generowana w SQLite?

Kolumna generowana to kolumna, której wartość jest wyliczana z wyrażenia korzystającego z innych kolumn tego samego wiersza. Deklarujesz ją przez GENERATED ALWAYS AS (expression) w CREATE TABLE. Nigdy nie zapisujesz do niej bezpośrednio: SQLite wylicza ją za ciebie przy każdym odczycie lub zapisie wiersza.

Czym różnią się kolumny generowane VIRTUAL i STORED?

Kolumna VIRTUAL jest wyliczana przy każdym odczycie i nie zajmuje miejsca na dysku; to wariant domyślny. Kolumna STORED jest wyliczana raz przy zapisie i zapisywana w pliku bazy, więc odczyty są tańsze, a zapisy nieco droższe. Obie można indeksować, ale STORED to zwykle dobry wybór, gdy wyrażenie jest ciężkie albo kolumna jest znacznie częściej czytana niż zapisywana.

Czy można zaindeksować kolumnę generowaną w SQLite?

Tak. CREATE INDEX działa na kolumnach generowanych, zarówno VIRTUAL, jak i STORED. To główny powód, żeby ich używać: możesz zaindeksować wartość pochodną (np. lower(email) albo pole JSON wyciągnięte przez ->>) i pozwolić planerowi zapytań korzystać z tego indeksu bez przepisywania każdego zapytania.

Czy można dodać kolumnę generowaną przez ALTER TABLE?

Tak, ale tylko kolumnę VIRTUAL. ALTER TABLE ... ADD COLUMN ... GENERATED ALWAYS AS (...) VIRTUAL działa bez problemu. Dodanie kolumny generowanej STORED przez ALTER TABLE nie jest obsługiwane: trzeba by przebudować tabelę. Planuj z wyprzedzeniem, jeśli chcesz mieć kolumny zapisywane w istniejących tabelach.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ