Query laporan yang memakai empat tabel dan GROUP BY tidak enak ditulis ulang setiap kali dibutuhkan. View mengatasinya dengan menyimpan query di dalam database dan memberinya nama, sehingga kita cukup memanggilnya seperti tabel biasa. Chapter ini membangun view ringkasan_pelanggan di atas relasi pelanggan-pesanan, membedah perilakunya yang selalu segar, lalu menguji batasannya: view bukan tabel, dan SQLite menolak semua upaya menulis ke dalamnya.
Skema Contoh #
Kita pakai kembali relasi one-to-many dari chapter Merancang Relasi One-to-Many:
CREATE TABLE pelanggan (
id INTEGER PRIMARY KEY,
nama TEXT NOT NULL,
kota TEXT
);
CREATE TABLE pesanan (
id INTEGER PRIMARY KEY,
tanggal TEXT NOT NULL,
pelanggan_id INTEGER REFERENCES pelanggan(id),
total INTEGER NOT NULL
);
INSERT INTO pelanggan VALUES
(1, 'Budi', 'Jakarta'),
(2, 'Siti', 'Bandung'),
(3, 'Ani', 'Jakarta');
INSERT INTO pesanan VALUES
(100, '2025-01-05', 1, 90000),
(101, '2025-01-06', 1, 25000),
(102, '2025-01-07', 2, 18000);Query Tersimpan Bernama #
Laporan “berapa banyak pesanan tiap pelanggan” biasanya ditulis sebagai LEFT JOIN plus GROUP BY. View berikut menyimpan query tersebut sekaligus memberi nama kolom hasilnya lewat daftar dalam tanda kurung:
CREATE VIEW ringkasan_pelanggan (id_pelanggan, nama_pelanggan, jumlah_pesanan, total_belanja) AS
SELECT p.id, p.nama, count(o.id), sum(o.total)
FROM pelanggan p
LEFT JOIN pesanan o ON o.pelanggan_id = p.id
GROUP BY p.id;Setelah dibuat, view dipakai persis seperti tabel:
SELECT * FROM ringkasan_pelanggan ORDER BY id_pelanggan;Output yang Dihasilkan:
1|Budi|2|115000
2|Siti|1|18000
3|Ani|0|Bedah singkat definisi view di atas:
- Baris 1 (
CREATE VIEW ringkasan_pelanggan (...)): nama view diikuti daftar nama kolom — tanpa daftar ini, kolom hasil mengikuti alias pada SELECT, yang untukcount(o.id)dansum(o.total)akan menjadi nama kolom yang buruk. - Baris 2-5 (
SELECT ... GROUP BY p.id): query apa pun yang valid boleh disimpan —JOIN,GROUP BY, hinggaUNION— dan akan dieksekusi ulang setiap kali view dipanggil.
View bukan salinan data. sqlite_master membuktikannya: yang tersimpan hanyalah teks CREATE VIEW-nya, bukan baris hasilnya:
SELECT sql FROM sqlite_master WHERE type = 'view' AND name = 'ringkasan_pelanggan';Output yang Dihasilkan:
CREATE VIEW ringkasan_pelanggan (id_pelanggan, nama_pelanggan, jumlah_pesanan, total_belanja) AS
SELECT p.id, p.nama, count(o.id), sum(o.total)
FROM pelanggan p
LEFT JOIN pesanan o ON o.pelanggan_id = p.id
GROUP BY p.idSelalu Segar #
Karena view mengeksekusi ulang query yang tersimpan, data terbaru selalu terlihat tanpa perintah sinkronisasi. Ani yang semula nol pesanan mendapat satu pesanan baru:
INSERT INTO pesanan VALUES (103, '2025-01-08', 3, 22000);SELECT * FROM ringkasan_pelanggan WHERE id_pelanggan = 3;Output yang Dihasilkan:
3|Ani|1|22000Tidak ada langkah refresh — baris pesanan baru langsung ikut terhitung pada pemanggilan view berikutnya.
Pola View Plus WHERE #
View tidak menerima parameter: SELECT * FROM ringkasan_pelanggan(1) bukan sintaks yang valid. Pola penggantinya adalah view sebagai tabel dasar, lalu saring di query pemanggil:
SELECT * FROM ringkasan_pelanggan WHERE total_belanja >= 50000;Output yang Dihasilkan:
1|Budi|2|115000Query di atas memakai total_belanja — kolom hasil agregat — sebagai kondisi WHERE, sesuatu yang tidak bisa dilakukan pada query aslinya tanpa HAVING atau subquery. Pola “view umum + WHERE sesuai kebutuhan” inilah yang membuat view berfungsi seperti laporan siap saring.
View Bersifat Read-Only #
View adalah query, bukan tabel — engine SQLite tidak tahu cara memetakan perubahan padanya kembali ke tabel sumbernya. Semua statement tulis ditolak:
UPDATE ringkasan_pelanggan SET total_belanja = 0 WHERE id_pelanggan = 1;Output yang Dihasilkan:
Parse error: cannot modify ringkasan_pelanggan because it is a viewINSERT dan DELETE pada view mendapat penolakan dengan pesan yang sama. Kalau memang butuh menulis melalui view, SQLite menyediakan trigger khusus INSTEAD OF yang mencegat statement tulis dan menggantinya dengan aksi Anda sendiri — dibahas pada chapter Trigger.
Menghapus View #
View dihapus dengan DROP VIEW, dan datanya tidak ikut hilang karena memang tidak pernah ada:
DROP VIEW ringkasan_pelanggan;SELECT count(*) FROM sqlite_master WHERE type = 'view';Output yang Dihasilkan:
0Sama seperti CREATE INDEX IF NOT EXISTS, tersedia DROP VIEW IF EXISTS untuk menghindari galat saat view memang tidak ada — berguna pada skrip skema yang dijalankan berulang.
Tabel bisa menyimpan kolom yang nilainya dihitung otomatis dari kolom lain — tanpa view dan tanpa kolom yang harus dijaga konsistensinya secara manual. Fitur tersebut dibahas pada chapter Generated Columns.