View

Query tersimpan sebagai objek, view yang dapat diperbarui, dan batasannya.

Daftar isi

View adalah objek skema berupa query bernama yang tersimpan di server, sehingga pernyataan SELECT yang panjang dapat dipanggil kembali seperti memanggil tabel biasa. Topik ini membahas pembuatan view, view yang dapat diperbarui (updatable view), pengaman WITH CHECK OPTION, batasan view yang memuat agregasi, serta pengelolaan definisinya. Setelah mengikuti topik ini, query berulang dapat dibungkus menjadi objek yang terdokumentasi dan konsisten di seluruh aplikasi.

Membuat dan membaca view#

CREATE VIEW menyimpan definisi query di bawah sebuah nama, dan setiap pembacaan nama itu menjalankan kembali querynya. Baris yang dikembalikan selalu mengikuti data tabel dasarnya saat itu, sehingga view berperilaku seperti jendela yang transparan, bukan salinan data yang membeku.

Jalankan persiapan berikut, kemudian buat view pertama.

sql persiapan-view.sql
-- tidak-dijalankan
-- Skrip persiapan topik view: basis data perpustakaan

DROP DATABASE IF EXISTS perpustakaan;
CREATE DATABASE perpustakaan;
USE perpustakaan;

CREATE TABLE anggota (
  id   INT AUTO_INCREMENT PRIMARY KEY,
  nama VARCHAR(100) NOT NULL
);

CREATE TABLE buku (
  id      INT AUTO_INCREMENT PRIMARY KEY,
  judul   VARCHAR(200) NOT NULL,
  penulis VARCHAR(100) NOT NULL,
  stok    INT NOT NULL DEFAULT 0
);

CREATE TABLE peminjaman (
  id              INT AUTO_INCREMENT PRIMARY KEY,
  anggota_id      INT NOT NULL,
  buku_id         INT NOT NULL,
  tanggal_pinjam  DATE NOT NULL,
  jatuh_tempo     DATE NOT NULL,
  tanggal_kembali DATE NULL,
  FOREIGN KEY (anggota_id) REFERENCES anggota(id),
  FOREIGN KEY (buku_id) REFERENCES buku(id)
);

INSERT INTO anggota (nama) VALUES
  ('Ali'), ('Budi'), ('Citra'), ('Dewi'), ('Eka');

INSERT INTO buku (judul, penulis, stok) VALUES
  ('Dasar Astronomi untuk Pemula', 'Hartono', 3),
  ('Sejarah Maritim Nusantara', 'Wijaya', 2),
  ('Panduan Menanam Sayuran', 'Santoso', 4),
  ('Pengantar Fisika Klasik', 'Kusuma', 1),
  ('Cerita Rakyat Nusantara', 'Lestari', 3),
  ('Ilmu Gizi Sehari-hari', 'Pratiwi', 2);

INSERT INTO peminjaman (anggota_id, buku_id, tanggal_pinjam, jatuh_tempo, tanggal_kembali) VALUES
  (1, 1, '2026-05-02', '2026-05-16', '2026-05-15'),
  (1, 2, '2026-05-02', '2026-05-16', '2026-05-14'),
  (1, 3, '2026-06-01', '2026-06-15', '2026-06-20'),
  (2, 4, '2026-05-10', '2026-05-24', '2026-05-22'),
  (2, 5, '2026-06-03', '2026-06-17', NULL),
  (2, 6, '2026-06-03', '2026-06-17', '2026-06-10'),
  (3, 1, '2026-05-20', '2026-06-03', '2026-06-01'),
  (3, 4, '2026-06-05', '2026-06-19', NULL),
  (4, 2, '2026-05-25', '2026-06-08', '2026-06-07'),
  (4, 3, '2026-06-12', '2026-06-26', NULL),
  (5, 4, '2026-06-14', '2026-06-28', '2026-06-27'),
  (5, 5, '2026-06-18', '2026-07-02', NULL),
  (3, 6, '2026-07-01', '2026-07-15', NULL);
bash
# membuat ulang basis data perpustakaan dari nol
mysql -u root < persiapan-view.sql
bash
# membuat view pinjam_aktif lalu membacanya seperti tabel biasa
mysql -t -u root perpustakaan <<'SQL'
DROP VIEW IF EXISTS pinjam_aktif;
CREATE VIEW pinjam_aktif AS
SELECT p.id, a.nama AS anggota, b.judul AS buku, p.tanggal_pinjam, p.jatuh_tempo
FROM peminjaman AS p
JOIN anggota AS a ON a.id = p.anggota_id
JOIN buku AS b ON b.id = p.buku_id
WHERE p.tanggal_kembali IS NULL;

SELECT * FROM pinjam_aktif ORDER BY id;
SQL
text Keluaran
+----+---------+-------------------------+----------------+-------------+
| id | anggota | buku                    | tanggal_pinjam | jatuh_tempo |
+----+---------+-------------------------+----------------+-------------+
|  5 | Budi    | Cerita Rakyat Nusantara | 2026-06-03     | 2026-06-17  |
|  8 | Citra   | Pengantar Fisika Klasik | 2026-06-05     | 2026-06-19  |
| 10 | Dewi    | Panduan Menanam Sayuran | 2026-06-12     | 2026-06-26  |
| 12 | Eka     | Cerita Rakyat Nusantara | 2026-06-18     | 2026-07-02  |
| 13 | Citra   | Ilmu Gizi Sehari-hari   | 2026-07-01     | 2026-07-15  |
+----+---------+-------------------------+----------------+-------------+

Query tiga tabel dengan saringan WHERE kini cukup dipanggil sebagai SELECT * FROM pinjam_aktif. Pola DROP VIEW IF EXISTS sebelum CREATE VIEW membuat skrip penyiapan dapat dijalankan ulang tanpa error, karena nama view yang sudah ada dihapus terlebih dahulu.

Pembaruan melalui view#

View yang hanya merujuk satu tabel tanpa agregasi dapat diperbarui. Pernyataan INSERT, UPDATE, dan DELETE yang ditujukan ke view diterjemahkan oleh MySQL menjadi pembaruan pada tabel dasarnya. Syaratnya sederhana, yaitu kolom yang dipetakan harus jelas sumber tabelnya.

bash
# view satu tabel dapat diperbarui seperti tabel biasa
mysql -t -u root perpustakaan <<'SQL'
DROP VIEW IF EXISTS buku_tersedia;
CREATE VIEW buku_tersedia AS
SELECT id, judul, penulis, stok
FROM buku
WHERE stok > 0;

UPDATE buku_tersedia SET stok = stok + 2 WHERE judul = 'Ilmu Gizi Sehari-hari';
SELECT id, judul, stok FROM buku_tersedia ORDER BY id;
SQL
text Keluaran
+----+------------------------------+------+
| id | judul                        | stok |
+----+------------------------------+------+
|  1 | Dasar Astronomi untuk Pemula |    3 |
|  2 | Sejarah Maritim Nusantara    |    2 |
|  3 | Panduan Menanam Sayuran      |    4 |
|  4 | Pengantar Fisika Klasik      |    1 |
|  5 | Cerita Rakyat Nusantara      |    3 |
|  6 | Ilmu Gizi Sehari-hari        |    4 |
+----+------------------------------+------+

Pembaruan melalui view menulis ke tabel buku, dan efeknya langsung terlihat. Namun ada celah yang perlu diketahui. Sisipan melalui view dengan saringan WHERE dapat menghasilkan baris yang tidak lolos saringan tersebut, sehingga barisnya masuk ke tabel dasar tetapi tidak pernah muncul di view.

bash
# sisipan melalui view tanpa WITH CHECK OPTION menghasilkan baris tersembunyi
mysql -t -u root perpustakaan <<'SQL'
INSERT INTO buku_tersedia (id, judul, penulis, stok) VALUES (7, 'Ensiklopedia Batu', 'Nugraha', 0);
SELECT id, judul, stok FROM buku_tersedia ORDER BY id;
SQL
text Keluaran
+----+------------------------------+------+
| id | judul                        | stok |
+----+------------------------------+------+
|  1 | Dasar Astronomi untuk Pemula |    3 |
|  2 | Sejarah Maritim Nusantara    |    2 |
|  3 | Panduan Menanam Sayuran      |    4 |
|  4 | Pengantar Fisika Klasik      |    1 |
|  5 | Cerita Rakyat Nusantara      |    3 |
|  6 | Ilmu Gizi Sehari-hari        |    4 |
+----+------------------------------+------+
bash
# baris baru itu nyata pada tabel dasarnya
mysql -t -u root perpustakaan -e "SELECT id, judul, stok FROM buku ORDER BY id;"
text Keluaran
+----+------------------------------+------+
| id | judul                        | stok |
+----+------------------------------+------+
|  1 | Dasar Astronomi untuk Pemula |    3 |
|  2 | Sejarah Maritim Nusantara    |    2 |
|  3 | Panduan Menanam Sayuran      |    4 |
|  4 | Pengantar Fisika Klasik      |    1 |
|  5 | Cerita Rakyat Nusantara      |    3 |
|  6 | Ilmu Gizi Sehari-hari        |    4 |
|  7 | Ensiklopedia Batu            |    0 |
+----+------------------------------+------+

Buku bernomor tujuh berstok nol lolos masuk ke tabel buku, tetapi tidak tampak di buku_tersedia karena saringannya menuntut stok > 0. Keadaan seperti ini membingungkan pembaca laporan, karena data tampak hilang tanpa jejak.

WITH CHECK OPTION#

WITH CHECK OPTION menutup celah tersebut. Dengan opsi ini, setiap sisipan dan pembaruan melalui view wajib menghasilkan baris yang tetap memenuhi syarat WHERE view itu, dan server menolak sisanya dengan error 1369.

bash
# WITH CHECK OPTION menolak baris yang tidak lolos syarat view
mysql -u root perpustakaan <<'SQL'
DROP VIEW IF EXISTS buku_tersedia;
CREATE VIEW buku_tersedia AS
SELECT id, judul, penulis, stok
FROM buku
WHERE stok > 0
WITH CHECK OPTION;
SQL

mysql -u root perpustakaan -e "INSERT INTO buku_tersedia (id, judul, penulis, stok) VALUES (8, 'Atlas Hujan Tropis', 'Mahendra', 0);"
text Keluaran
ERROR 1369 (HY000) at line 1: CHECK OPTION failed 'perpustakaan.buku_tersedia'

Tanpa opsi ini, sisipan serupa akan lolos dan menghasilkan baris tersembunyi seperti pada bagian sebelumnya. Secara bawaan pemeriksaan berlaku bertingkat (cascaded), yaitu syarat view ini dan seluruh view lain yang menjadi dasarnya diperiksa sekaligus.

View yang tidak dapat diperbarui#

View yang mengandung agregasi tidak lagi punya pemetaan baris yang jelas ke tabel dasarnya, sehingga tidak dapat diperbarui. View statistik_buku berikut memakai GROUP BY, dan percobaan pembaruannya ditolak server.

bash
# view dengan GROUP BY tidak dapat diperbarui
mysql -u root perpustakaan <<'SQL'
DROP VIEW IF EXISTS statistik_buku;
CREATE VIEW statistik_buku AS
SELECT penulis, COUNT(*) AS jumlah_judul, SUM(stok) AS total_stok
FROM buku
GROUP BY penulis;
SQL

mysql -u root perpustakaan -e "UPDATE statistik_buku SET total_stok = 9 WHERE penulis = 'Nugraha';"
text Keluaran
ERROR 1288 (HY000) at line 1: The target table statistik_buku of the UPDATE is not updatable

Selain GROUP BY, view menjadi tidak dapat diperbarui apabila definisinya memuat fungsi agregat, DISTINCT, HAVING, UNION, atau subquery pada daftar kolomnya. View semacam itu tetap sangat berguna sebagai lapisan pembacaan, hanya saja perubahannya harus ditulis ke tabel dasarnya.

Mengelola view#

Definisi view dapat diganti dengan ALTER VIEW tanpa menghapus hak akses dan tanpa memaksa pemanggilnya menyesuaikan nama. Penghapusan memakai DROP VIEW, dan status seluruh view dapat diperiksa lewat kamus data information_schema.views.

bash
# definisi view dapat diganti tanpa mengubah nama objeknya
mysql -t -u root perpustakaan <<'SQL'
ALTER VIEW statistik_buku AS
SELECT penulis, SUM(stok) AS total_stok
FROM buku
GROUP BY penulis;

SELECT * FROM statistik_buku ORDER BY penulis;
SQL
text Keluaran
+---------+------------+
| penulis | total_stok |
+---------+------------+
| Hartono |          3 |
| Kusuma  |          1 |
| Lestari |          3 |
| Nugraha |          0 |
| Pratiwi |          4 |
| Santoso |          4 |
| Wijaya  |          2 |
+---------+------------+
bash
# status view terdaftar pada kamus data
mysql -t -u root perpustakaan -e "SELECT table_name, is_updatable FROM information_schema.views WHERE table_schema = 'perpustakaan' ORDER BY table_name;"
text Keluaran
+----------------+--------------+
| TABLE_NAME     | IS_UPDATABLE |
+----------------+--------------+
| buku_tersedia  | YES          |
| pinjam_aktif   | YES          |
| statistik_buku | NO           |
+----------------+--------------+

Kolom IS_UPDATABLE melaporkan sifat setiap view. buku_tersedia bernilai YES karena merujuk satu tabel tanpa agregasi, dan pinjam_aktif juga bernilai YES karena join tanpa agregasi tetap mempertahankan pemetaan barisnya. UPDATE melalui view join diterima selama perubahan menyentuh satu tabel dasar saja. INSERT dan DELETE pada view multi-tabel ditolak karena barisnya melibatkan lebih dari satu tabel dasar sekaligus. statistik_buku mengelompokkan baris dengan GROUP BY, sehingga pemetaan barisnya hilang dan nilainya NO.

Kesalahan umum#

Kesalahan pertama adalah menganggap view sebagai tabel fisik yang mempercepat query. Pembacaan view menjalankan kembali query dasarnya, sehingga view di atas query lambat tetap lambat. Apabila hasil perantara memang perlu dibekukan, CREATE TABLE ... AS SELECT atau tabel ringkasan yang diisi berkala lebih tepat daripada view.

Kesalahan kedua adalah menyisipkan data melalui view bersyarat tanpa WITH CHECK OPTION, yang menghasilkan baris tersembunyi seperti telah didemonstrasikan. Kesalahan ketiga adalah mencoba memperbarui view agregat, yang selalu berujung error 1288. Perbaikannya bukan mengubah view, melainkan menulis pembaruan ke tabel dasarnya. Kesalahan keempat adalah mengabaikan pola DROP VIEW IF EXISTS pada skrip penyiapan, sehingga skrip hanya bisa dijalankan sekali dan gagal pada eksekusi berikutnya.

Ringkasan#

  • View adalah query bernama yang tersimpan sebagai objek skema, dan setiap pembacaannya menjalankan kembali query dasarnya terhadap data terkini.
  • View satu tabel tanpa agregasi dapat diperbarui, dan WITH CHECK OPTION menolak sisipan maupun pembaruan yang menghasilkan baris di luar syaratnya.
  • View dengan agregasi, DISTINCT, UNION, atau subquery pada daftar kolomnya tidak dapat diperbarui dan melaporkan dirinya sebagai IS_UPDATABLE = NO, sedangkan view join tanpa agregasi menerima UPDATE pada satu tabel dasar dalam satu pernyataan.
  • Definisi view dikelola dengan ALTER VIEW dan DROP VIEW, dengan pola DROP VIEW IF EXISTS agar skrip tetap dapat dijalankan ulang.

Referensi#