Po co istnieją tabele STRICT
Domyślne podejście SQLite do typów jest słynne ze swojej swobody. Zadeklaruj kolumnę INTEGER, wstaw tekst "hello", a SQLite wzruszy ramionami i zapisze tekst. Ta elastyczność była świadomą decyzją projektową z lat 90., ale zaskakuje osoby przychodzące z Postgres czy MySQL i ukrywa błędy.
Tabele STRICT, dodane w SQLite 3.37, to naprawiają. Włączasz je dla konkretnej tabeli i od tej chwili typy kolumn znaczą to, co mówią.
Słowo kluczowe STRICT stoi po nawiasie zamykającym. Cała reszta wygląda jak zwykłe CREATE TABLE. Różnica wychodzi na jaw w chwili, gdy spróbujesz wstawić do kolumny wartość złego rodzaju.
Co właściwie egzekwuje STRICT
W zwykłej tabeli powinowactwo typów próbuje skonwertować wartość do zadeklarowanego typu, a gdy się nie da, zapisuje ją bez zmian. W tabeli STRICT niezgodność to błąd.
Zrób to samo na tabeli bez STRICT, a trzecie wstawienie się uda: SQLite radośnie zapisze tekst 'oops' w kolumnie, którą oznaczono jako INTEGER. Po miesiącach zapytanie agregujące zwróci bzdury, a ty spędzisz popołudnie na szukaniu przyczyny. STRICT sprawia, że błąd pojawia się już przy wstawianiu, gdzie możesz go naprawić.
Błąd, który zobaczysz:
Runtime error: cannot store TEXT value in INTEGER column accounts.balance
Jasny, natychmiastowy i trudny do zignorowania.
Pięć dozwolonych typów
Tabele STRICT akceptują tylko pięć nazw typów:
INTEGER: liczby całkowite.REAL: liczby zmiennoprzecinkowe.TEXT: teksty.BLOB: surowe bajty.ANY: dowolny typ, bez konwersji.
To wszystko. Swobodne aliasy, które SQLite normalnie akceptuje (VARCHAR(255), DOUBLE, BOOLEAN, DATETIME, INT), w tabeli STRICT zgłaszają błąd:
Błąd:
Parse error: unknown datatype for bad.name: "VARCHAR(255)"
Rozwiązanie to użycie jednej z pięciu kanonicznych nazw. VARCHAR(255) zmienia się w TEXT, DATETIME w TEXT (SQLite i tak przechowuje daty jako teksty ISO), a BOOLEAN w INTEGER (z 0 i 1).
Furtka ANY
ANY to jedyny typ, który pozwala kolumnie STRICT przechowywać wartości różnych typów. Przydaje się na przykład w ogólnej kolumnie value w tabeli klucz-wartość:
ANY jest w tabelach STRICT wyjątkowy: przechowuje wartości bez wymuszania typu, które to samo słowo oznaczałoby gdzie indziej. Tekst '100' zostaje tekstem, a liczba całkowita 100 zostaje liczbą całkowitą. Dowodzą tego wywołania typeof() w zapytaniu powyżej.
W tabeli bez STRICT kolumna z powinowactwem ANY zamieniałaby teksty wyglądające na liczby w liczby. STRICT dokładnie zachowuje pierwotny typ.
STRICT i PRIMARY KEY
Jedna subtelna różnica: w zwykłej tabeli INTEGER PRIMARY KEY jest wyjątkowy, bo staje się aliasem rowid i przyjmuje tylko liczby całkowite. Inne deklaracje klucza głównego są luźniejsze.
W tabeli STRICT typ kolumny jest egzekwowany niezależnie od tego, czy jest ona kluczem głównym:
Drugie wstawienie się nie udaje. W tabeli bez STRICT 42 zostałoby po cichu zapisane w kolumnie klucza głównego typu TEXT. Tutaj dostajesz komunikat.
Łączenie tabel STRICT i zwykłych
STRICT działa na poziomie tabeli, a nie bazy. W jednym pliku możesz mieć ścisłą tabelę users i swobodną tabelę events. Klucze obce działają między nimi tak samo jak zawsze.
Tabela events nie ma STRICT ani zadeklarowanego typu dla payload, więc przyjmuje wszystko, co do niej wrzucisz. Czasem przydatne, ale ryzykowne jako ustawienie domyślne. Zostaw nietypowane przechowywanie na przypadki, w których naprawdę potrzebujesz kolumny na wszystko.
Kiedy używać STRICT
Przy nowych schematach odpowiedź brzmi: "prawie zawsze". Koszt jest mały: jedno słowo kluczowe na tabelę i zapamiętanie pięciu kanonicznych nazw typów. Korzyść jest taka, że błędy, które normalnie czaiłyby się w danych, ujawniają się przy wstawieniu, które je spowodowało.
Pomiń STRICT, gdy:
- Utrzymujesz starą bazę SQLite, której istniejący schemat opiera się na swobodnym typowaniu.
- Celujesz w SQLite starsze niż 3.37 (październik 2021): tam to słowo kluczowe nie istnieje.
- Naprawdę chcesz, żeby kolumna przechowywała mieszane typy. Nawet wtedy lepiej wybrać
STRICTz kolumnąANYniż tabelę bez STRICT, bo cała reszta pozostaje egzekwowana.
Krótka lista kontrolna przy zamianie zwykłej tabeli na STRICT:
- Zamień
VARCHAR,CHAR,NVARCHARnaTEXT. - Zamień
DOUBLE,FLOAT,NUMERICnaREAL. - Zamień
BOOLEAN,BIT,TINYINTnaINTEGER. - Zamień
DATETIME,TIMESTAMP,DATEnaTEXT(alboINTEGER, jeśli przechowujesz uniksowe znaczniki czasu). - Dopisz
STRICTpo nawiasie zamykającym.
Dalej: klucze główne
Tabele STRICT zaostrzają sposób, w jaki kolumny przechowują dane. Następna rzecz, którą warto zaostrzyć, to która kolumna identyfikuje każdy wiersz. Klucze główne w SQLite mają kilka dziwactw (zwłaszcza wokół INTEGER PRIMARY KEY i rowid), które warto znać, zanim zaprojektujesz prawdziwy schemat.
Najczęściej zadawane pytania
Czym jest tabela STRICT w SQLite?
Tabela STRICT egzekwuje zadeklarowany typ kolumny: jeśli kolumna ma typ INTEGER, SQLite odrzuci każdą wartość, która nie jest liczbą całkowitą ani NULL. Włączasz to, dopisując słowo kluczowe STRICT po nawiasie zamykającym CREATE TABLE. Bez niego SQLite używa powinowactwa typów, które konwertuje wartości, gdy się da, a gdy się nie da, zapisuje je bez zmian.
Jakich typów mogę używać w tabeli STRICT?
Tylko pięciu: INTEGER, REAL, TEXT, BLOB i ANY. Aliasy działające w zwykłych tabelach (VARCHAR, DOUBLE, BOOLEAN, DATETIME) w tabeli STRICT zgłaszają błąd. Kolumna ANY to furtka, która przyjmuje dowolny typ bez konwersji.
Czy w nowych bazach SQLite używać tabel STRICT?
W przypadku większości nowych schematów tak. Tabele STRICT wyłapują błędy, które zwykłe tabele po cichu przełykają: zabłąkany tekst w kolumnie INTEGER albo listę przypadkiem zserializowaną do REAL. Kosztuje to jedno dodatkowe słowo kluczowe na tabelę i rezygnację z egzotycznych nazw typów. Dostępne od SQLite 3.37 (2021).