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ć
INSERTaniUPDATEna kolumnie generowanej.INSERT INTO products (total) VALUES (5)to błąd. - Kolumn
STOREDnie można dodać przezALTER TABLE ... ADD COLUMN. Po fakcie można dodać tylkoVIRTUAL. - Kolumny generowane mogą mieć ograniczenia
NOT NULL,CHECK,UNIQUE, a nawetFOREIGN 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.