Pada kebanyakan database, INTEGER adalah kontrak yang keras: masukkan teks ke kolom integer dan database menolaknya. SQLite memilih jalan lain — kolom bertipe tetap, tetapi engine berusaha menyesuaikan nilai yang masuk dengan konversi otomatis. Perilaku ini bernama type affinity, dan memahaminya adalah kunci untuk tidak terkejut oleh data yang tersimpan dalam bentuk tak terduga. Chapter ini membedah aturan mainnya, semuanya diverifikasi langsung dengan fungsi typeof().

Lima Storage Class #

Di level paling dasar, SQLite hanya mengenal lima storage class — bentuk penyimpanan nilai di dalam berkas database:

Storage classIsiContoh literal
NULLNilai kosongNULL
INTEGERBilangan bulat42
REALBilangan pecahan3.14
TEXTTeks'Budi'
BLOBData mentahx'89504E47'

Fungsi typeof() melaporkan storage class dari nilai atau isi kolom. Kita bisa mencobanya langsung pada nilai literal:

text
sqlite> SELECT typeof(NULL), typeof(42), typeof(3.14), typeof('Budi'), typeof(x'89504E47');
null|integer|real|text|blob
sqlite>

Yang mengejutkan pemula SQLite: tipe kolom seperti INTEGER atau TEXT di statement CREATE TABLE bukanlah penjara. Fungsi tipe kolom hanyalah rekomendasi konversi, dan rekomendasi itulah yang disebut type affinity.

Aturan Type Affinity #

Saat sebuah nilai dimasukkan ke kolom, engine SQLite menentukan konversinya dari tipe yang kita tulis saat membuat tabel. Aturannya diperiksa berurutan — aturan pertama yang cocok menang:

  1. Nama tipe mengandung INT → affinity INTEGER.
  2. Nama tipe mengandung CHAR, CLOB, atau TEXT → affinity TEXT.
  3. Nama tipe BLOB atau tidak bertipe sama sekali → affinity BLOB.
  4. Nama tipe mengandung REAL, FLOA, atau DOUB → affinity REAL.
  5. Nama tipe lainnya → affinity NUMERIC.

Dua nama tipe yang sering mengejutkan: VARCHAR(10) dan CHAR(10) sama-sama mengandung CHAR, jadi keduanya beraffinity TEXT — bukan numerik. Sebaliknya DECIMAL(10,2) tidak memuat satu pun kata kunci di atas, jadi jatuh ke affinity NUMERIC. Tabel berikut merangkum pemetaan yang paling sering dipakai:

Deklarasi kolomAffinityContoh lain
INT, INTEGER, BIGINTINTEGERSMALLINT, TINYINT
TEXT, CHAR(10), VARCHAR(255)TEXTCLOB
BLOB, tanpa tipeBLOB—
REAL, FLOAT, DOUBLE PRECISIONREALDOUBLE
DECIMAL(10,2), BOOLEAN, DATENUMERICNUMERIC

Affinity inilah yang menentukan apa yang sebenarnya tersimpan. Mari kita buktikan dengan satu tabel uji berisi tiga kolom beraffinity berbeda:

text
sqlite> CREATE TABLE uji (
   ...>   a INTEGER,
   ...>   b REAL,
   ...>   c TEXT
   ...> );
sqlite> INSERT INTO uji VALUES ('10', 2, 100);
sqlite> SELECT typeof(a), typeof(b), typeof(c) FROM uji;
integer|real|text
sqlite>

Satu statement INSERT ini memuat tiga kejutan sekaligus:

  1. Kolom a INTEGER diisi string '10' — affinity INTEGER mengonversi teks yang membentuk bilangan bulat menjadi integer, sehingga yang tersimpan adalah integer 10.
  2. Kolom b REAL diisi bilangan bulat 2 — affinity REAL mengubahnya menjadi pecahan 2.0.
  3. Kolom c TEXT diisi bilangan bulat 100 — affinity TEXT mengubahnya menjadi teks '100'.

Affinity NUMERIC berperilaku seperti INTEGER bila bisa: '10' menjadi integer 10 dan '3.14' menjadi real 3.14. Teks yang tak bisa dikonversi tetap disimpan sebagai teks — tanpa error:

text
sqlite> CREATE TABLE uji2 (n NUMERIC);
sqlite> INSERT INTO uji2 VALUES ('10'), ('3.14'), ('Budi');
sqlite> SELECT n, typeof(n) FROM uji2;
10|integer
3.14|real
Budi|text
sqlite>

STRICT untuk Pengetikan yang Kaku #

Jika fleksibilitas ini justru berbahaya bagi aplikasi kita, SQLite sejak versi 3.37.0 menyediakan mode ketat: tambahkan kata STRICT setelah kurung penutup CREATE TABLE. Pada STRICT table, tipe kolom wajib salah satu dari INT, INTEGER, REAL, TEXT, BLOB, atau ANY — nama lain seperti VARCHAR(10) langsung ditolak.

text
sqlite> CREATE TABLE produk_strict (
   ...>   id INTEGER PRIMARY KEY,
   ...>   nama TEXT NOT NULL,
   ...>   harga INTEGER NOT NULL
   ...> ) STRICT;
sqlite> INSERT INTO produk_strict VALUES (1, 'Pensil', 3000);
sqlite> INSERT INTO produk_strict VALUES (2, 'Buku Tulis', 'murah');
Runtime error: cannot store TEXT value in INTEGER column produk_strict.harga (19)
sqlite>

Insert kedua ditolak keras: teks 'murah' tidak mungkin dikonversi menjadi integer tanpa kehilangan makna. Teks yang membentuk angka sah — misalnya '5500' — tetap diterima karena konversinya lossless, tetapi teks sembarangan tidak lolos. Pilihan antara tabel biasa dan STRICT tabel sepenuhnya ada di tangan kita: tabel biasa mewarisi fleksibilitas khas SQLite, sedangkan STRICT memberi perilaku yang segerak dengan PostgreSQL atau MySQL.

Bagaimana pun jenis tabel yang kita pilih, struktur kolom saja belum cukup menjaga kualitas data — kolom nama masih boleh kosong dan dua baris produk masih boleh bernama sama. Chapter Constraint pada Tabel melengkapi pertahanan itu.