Kolom seperti subtotal = harga * jumlah sering disimpan secara manual atau dihitung ulang di aplikasi — dua-duanya rawan: lupa menghitung, dan nilainya melenceng dari kolom sumbernya. Generated columns menyelesaikannya di dalam tabel itu sendiri: nilainya dihitung engine SQLite dari kolom lain pada baris yang sama, setiap saat, tanpa pernah bisa tidak sinkron. Fitur ini hadir sejak SQLite 3.31.0 (Januari 2020).
Kolom yang Dihitung Otomatis #
Tabel detail_pesanan berikut menyerahkan perhitungan subtotal kepada engine SQLite:
CREATE TABLE detail_pesanan (
id INTEGER PRIMARY KEY,
nama_produk TEXT NOT NULL,
harga INTEGER NOT NULL,
jumlah INTEGER NOT NULL,
subtotal INTEGER GENERATED ALWAYS AS (harga * jumlah)
);
INSERT INTO detail_pesanan (id, nama_produk, harga, jumlah) VALUES
(1, 'Kopi Gayo', 45000, 2),
(2, 'Gula Aren', 25000, 1),
(3, 'Teh Melati', 18000, 3);SELECT id, nama_produk, harga, jumlah, subtotal FROM detail_pesanan ORDER BY id;Output yang Dihasilkan:
1|Kopi Gayo|45000|2|90000
2|Gula Aren|25000|1|25000
3|Teh Melati|18000|3|54000Bedah singkat definisi kolom subtotal:
GENERATED ALWAYS AS (harga * jumlah): ekspresi boleh membaca kolom lain pada baris yang sama, fungsi deterministik sepertiupper()danprintf(), sertaCASE— tetapi tidak boleh membaca kolom baris lain.- Tanpa kata
VIRTUAL/STORED: nilai bawaannyaVIRTUAL— hasil dihitung setiap kali kolom dibaca, tidak menyimpan apa pun di dalam berkas database.
Perhatikan juga INSERT di atas: daftar kolomnya sengaja tidak memuat subtotal, dan memang tidak boleh — kolom hasil perhitungan tidak menerima nilai dari luar:
INSERT INTO detail_pesanan (id, nama_produk, harga, jumlah, subtotal)
VALUES (4, 'Sirup Vanila', 30000, 2, 999999);Output yang Dihasilkan:
Parse error: cannot INSERT into generated column "subtotal"Perubahan pada kolom sumber otomatis terpakai ulang. Jumlah Gula Aren dinaikkan menjadi 4:
UPDATE detail_pesanan SET jumlah = 4 WHERE id = 2;SELECT subtotal FROM detail_pesanan WHERE id = 2;Output yang Dihasilkan:
100000VIRTUAL versus STORED #
Menambahkan kata STORED membuat hasil perhitungan benar-benar disimpan di berkas database saat baris ditulis:
CREATE TABLE harga_khusus (
id INTEGER PRIMARY KEY,
nama_produk TEXT NOT NULL,
harga INTEGER NOT NULL,
diskon_persen INTEGER NOT NULL,
harga_akhir INTEGER GENERATED ALWAYS AS (harga * (100 - diskon_persen) / 100) STORED
);
INSERT INTO harga_khusus (id, nama_produk, harga, diskon_persen) VALUES
(1, 'Kopi Gayo', 50000, 20),
(2, 'Teh Melati', 20000, 15);SELECT id, nama_produk, harga, diskon_persen, harga_akhir FROM harga_khusus ORDER BY id;Output yang Dihasilkan:
1|Kopi Gayo|50000|20|40000
2|Teh Melati|20000|15|17000| Aspek | VIRTUAL | STORED |
|---|---|---|
| Penyimpanan | Tidak menyimpan nilai | Nilai ditulis ke berkas database |
| Saat dihitung | Setiap kali dibaca | Saat baris ditulis atau diubah |
| Kecepatan baca | Sedikit kerja ekstra per baris | Cepat dibaca seperti kolom biasa |
| Biaya tulis dan ukuran berkas | Ringan | Lebih berat |
Pilihan praktisnya: VIRTUAL untuk perhitungan ringan seperti perkalian dua kolom; STORED untuk perhitungan berat yang dibaca jauh lebih sering daripada ditulis.
Merujuk Kolom Generated Lain #
Ekspresi generated column boleh membaca generated column lain pada baris yang sama, dan STORED pun boleh merujuk VIRTUAL. Tabel berikut memakai dua lapis perhitungan:
CREATE TABLE cek_stok (
id INTEGER PRIMARY KEY,
stok INTEGER NOT NULL,
ambang INTEGER NOT NULL,
kurang INTEGER GENERATED ALWAYS AS (stok - ambang) VIRTUAL,
peringatan INTEGER GENERATED ALWAYS AS (CASE WHEN kurang < 0 THEN 1 ELSE 0 END) STORED
);
INSERT INTO cek_stok (id, stok, ambang) VALUES
(1, 10, 15),
(2, 20, 15);SELECT id, stok, ambang, kurang, peringatan FROM cek_stok ORDER BY id;Output yang Dihasilkan:
1|10|15|-5|1
2|20|15|5|0Kolom peringatan membaca kurang — hasil perhitungan stok - ambang — lalu memutuskan sendiri nilainya lewat CASE. Karena ekspresinya harus deterministik, hasil setiap baris selalu bisa direproduksi.
Larangan pada Generated Columns #
Ada empat larangan yang langsung ditolak engine SQLite saat CREATE TABLE. Generated column tidak boleh menjadi primary key:
CREATE TABLE t_pk (
a INTEGER,
b INTEGER GENERATED ALWAYS AS (a + 1) PRIMARY KEY
);Output yang Dihasilkan:
Parse error: generated columns cannot be part of the PRIMARY KEYSubquery dilarang karena bisa membaca baris lain:
CREATE TABLE t_sub (
a INTEGER,
b INTEGER GENERATED ALWAYS AS ((SELECT max(x) FROM t))
);Output yang Dihasilkan:
Parse error: subqueries prohibited in generated columnsFungsi non-deterministic dilarang karena hasilnya berubah-ubah:
CREATE TABLE t_rand (
a INTEGER,
b INTEGER GENERATED ALWAYS AS (abs(random()))
);Output yang Dihasilkan:
Parse error: non-deterministic functions prohibited in generated columns
a INTEGER, b INTEGER GENERATED ALWAYS AS (abs(random())) );
error here ---^DEFAULT juga tidak berlaku karena nilainya memang ditentukan ekspresi:
CREATE TABLE t_def (
a INTEGER,
b INTEGER GENERATED ALWAYS AS (a + 1) DEFAULT 5
);Output yang Dihasilkan:
Parse error: cannot use DEFAULT on a generated columnMenambah Kolom Generated pada Tabel Berisi Data #
ALTER TABLE ADD COLUMN menerima kolom VIRTUAL meskipun tabelnya sudah berisi baris:
CREATE TABLE nota_baru (
id INTEGER PRIMARY KEY,
nama TEXT NOT NULL
);
INSERT INTO nota_baru VALUES (1, 'Nota A');
ALTER TABLE nota_baru ADD COLUMN total INTEGER GENERATED ALWAYS AS (id * 1000) VIRTUAL;SELECT * FROM nota_baru ORDER BY id;Output yang Dihasilkan:
1|Nota A|1000Kolom STORED menemui batasan di sini — SQLite menolak menambahkannya pada tabel yang sudah berisi data:
ALTER TABLE nota_baru ADD COLUMN pajak INTEGER GENERATED ALWAYS AS (id / 10) STORED;Output yang Dihasilkan:
Runtime error: cannot add a STORED columnDefinisi generated columns terlihat utuh pada .schema:
sqlite> .schema detail_pesanan
CREATE TABLE detail_pesanan (
id INTEGER PRIMARY KEY,
nama_produk TEXT NOT NULL,
harga INTEGER NOT NULL,
jumlah INTEGER NOT NULL,
subtotal INTEGER GENERATED ALWAYS AS (harga * jumlah)
);Dengan generated columns, desain database tidak berhenti di struktur statis — tabel Anda kini membawa logika perhitungan yang selalu konsisten. Section Fungsi dan Fitur Lanjutan melanjutkan dengan fitur SQL SQLite yang lebih dalam: fungsi scalar, tanggal dan waktu, JSON, UPSERT, trigger, hingga full-text search.