Constraint adalah aturan yang menyertai definisi kolom atau tabel untuk membatasi nilai yang boleh disimpan. Tanpa constraint, keabsahan data sepenuhnya bergantung pada kedisiplinan setiap program yang menulis ke basis data. Topik ini membahas PRIMARY KEY, NOT NULL, UNIQUE, CHECK, dan DEFAULT, beserta pesan error nyata yang muncul ketika masing-masing aturan dilanggar.
PRIMARY KEY sebagai alias rowid#
Setiap tabel biasa di SQLite memiliki kolom tersembunyi bernama rowid, yaitu bilangan bulat unik yang menandai setiap baris. Kolom yang dideklarasikan INTEGER PRIMARY KEY menjadi alias rowid, sehingga membaca kolom itu sama dengan membaca rowidnya. Blok berikut membuat tabel kategori, memasukkan tiga baris tanpa nilai id maupun dengan id NULL, lalu menampilkan keduanya berdampingan.
CREATE TABLE kategori (
id INTEGER PRIMARY KEY,
nama TEXT NOT NULL
);
INSERT INTO kategori (nama) VALUES ('Sains'), ('Sastra');
INSERT INTO kategori (id, nama) VALUES (NULL, 'Sejarah');
SELECT id, rowid, nama FROM kategori;1|1|Sains
2|2|Sastra
3|3|SejarahKeluaran menunjukkan bahwa id dan rowid bernilai sama pada setiap baris. SQLite mengisi nilai tersebut secara otomatis dengan bilangan yang lebih besar daripada nilai rowid terbesar yang sedang dipakai, sehingga program tidak perlu menomori baris secara manual. Pengenal bertipe ini menjadi titik rujukan yang stabil bagi tabel lain, misalnya tabel peminjaman yang menyimpan id buku. Alias tersebut juga cepat, karena pencarian berdasarkan rowid adalah pencarian langsung pada struktur penyimpanan tabel.
Nilai rowid sebuah tabel biasa dapat berubah ketika tabelnya dibangun ulang, tetapi nilai kolom alias tidak, karena kolom itu tersimpan sebagai kolom biasa pada berkas basis data. Deklarasi INTEGER PRIMARY KEY dengan demikian menjadi cara yang sah untuk menetapkan pengenal permanen. Kedua sifat tersebut menjadikannya pilihan bawaan untuk kolom id pada hampir semua skema SQLite.
NOT NULL dan DEFAULT#
Constraint NOT NULL menolak NULL pada kolomnya, baik saat INSERT maupun saat UPDATE. Pelanggaran menghentikan pernyataan yang bersangkutan dan menghasilkan pesan error yang menyebut tabel serta kolomnya. Blok berikut mencoba memasukkan anggota tanpa nama pada kolom yang dideklarasikan nama TEXT NOT NULL.
CREATE TABLE anggota (
id INTEGER PRIMARY KEY,
nama TEXT NOT NULL,
email TEXT UNIQUE
);
INSERT INTO anggota (id, email) VALUES (1, '[email protected]');Error near line 7: NOT NULL constraint failed: anggota.namaConstraint DEFAULT bekerja pada arah yang berlawanan. Apabila kolom ditinggalkan pada pernyataan INSERT, SQLite mengisinya dengan nilai bawaan yang dideklarasikan. Blok berikut memasukkan dua anggota, satu tanpa kolom aktif dan satu dengan nilai eksplisit.
CREATE TABLE anggota (
id INTEGER PRIMARY KEY,
nama TEXT NOT NULL,
aktif INTEGER NOT NULL DEFAULT 1
);
INSERT INTO anggota (nama) VALUES ('Dewi');
INSERT INTO anggota (nama, aktif) VALUES ('Farhan', 0);
SELECT id, nama, aktif FROM anggota;1|Dewi|1
2|Farhan|0Baris pertama memperlihatkan nilai bawaan 1 yang terisi otomatis, sedangkan baris kedua memperlihatkan nilai eksplisit 0 yang menggantikannya. Nilai bawaan dievaluasi pada saat baris dimasukkan, bukan saat tabel dibuat. Perlu dicatat bahwa DEFAULT tidak menolak NULL yang dikirim secara eksplisit, sehingga kolom yang harus selalu terisi memerlukan kombinasi NOT NULL DEFAULT seperti pada contoh.
Pesan error NOT NULL selalu menyebut tabel dan kolom yang bermasalah, sehingga penyebabnya mudah dilacak pada definisi. Constraint ini juga berlaku saat UPDATE, bukan hanya saat INSERT, sehingga kolom yang sudah terisi tidak dapat dikosongkan kembali. Kombinasi keduanya dengan demikian menjadi pola standar untuk kolom status yang wajib terisi namun tetap nyaman ditulis.
UNIQUE dan pengecualiannya#
Constraint UNIQUE melarang dua baris bernilai sama pada kolomnya. Pelanggaran pertama menghasilkan error yang menyebut kolom tersebut, sebagaimana ketika dua anggota mencoba memakai alamat surel yang sama.
CREATE TABLE anggota (
id INTEGER PRIMARY KEY,
nama TEXT NOT NULL,
email TEXT UNIQUE
);
INSERT INTO anggota (nama, email) VALUES ('Budi', '[email protected]');
INSERT INTO anggota (nama, email) VALUES ('Citra', '[email protected]');Error near line 9: UNIQUE constraint failed: anggota.emailConstraint ini memiliki satu pengecualian yang dikenal luas, yaitu NULL tidak dianggap sama dengan NULL. Beberapa baris dengan NULL pada kolom UNIQUE tetap diterima, karena perbandingan terhadap NULL tidak menghasilkan nilai benar.
CREATE TABLE anggota (
id INTEGER PRIMARY KEY,
nama TEXT NOT NULL,
email TEXT UNIQUE
);
INSERT INTO anggota (nama, email) VALUES ('Dewi', NULL), ('Eka', NULL);
SELECT id, nama, email FROM anggota;1|Dewi|
2|Eka|Pada keluaran, posisi surel yang kosong menandakan NULL, dan kedua baris tetap diterima meskipun keduanya tidak mempunyai surel. Perilaku ini mengikuti semantik perbandingan SQL, yang menyatakan bahwa perbandingan apa pun dengan NULL tidak menghasilkan nilai benar. Kolom UNIQUE dengan demikian cocok untuk data opsional seperti surel yang boleh tidak dimiliki sebagian anggota, dan setiap nilai non-NULL yang berulang tetap ditolak.
UNIQUE juga dapat diletakkan pada tingkat tabel untuk melarang duplikasi kombinasi, misalnya UNIQUE (buku_id, anggota_id) yang membatasi satu peminjaman per pasangan buku dan anggota. Bentuk tingkat tabel ini menjadi penting ketika aturannya melibatkan lebih dari satu kolom sekaligus.
CHECK untuk aturan nilai#
Constraint CHECK mengevaluasi sebuah ekspresi pada setiap INSERT dan UPDATE, dan baris hanya diterima bila ekspresinya bernilai benar atau NULL. Ekspresi boleh melibatkan beberapa kolom pada baris yang sama, sehingga CHECK cocok untuk aturan seperti batas rentang tahun terbit. Constraint ini dapat diberi nama melalui CONSTRAINT, dan nama itulah yang muncul pada pesan errornya.
CREATE TABLE buku (
id INTEGER PRIMARY KEY,
judul TEXT NOT NULL,
tahun INTEGER,
CONSTRAINT cek_tahun CHECK (tahun IS NULL OR tahun >= 1800)
);
INSERT INTO buku (judul, tahun) VALUES ('Naskah Kuno', 1700);
INSERT INTO buku (judul, tahun) VALUES ('Peta Langit', 2021);
SELECT judul, tahun FROM buku;Error near line 8: CHECK constraint failed: cek_tahun
Peta Langit|2021Pesan error menyebut nama constraint cek_tahun, sehingga aturan yang dilanggar mudah dilacak pada definisi tabel. Ekspresi pada CHECK ditulis dengan operator perbandingan dan logika yang sama seperti klausa WHERE, sehingga tidak ada sintaks baru yang perlu dipelajari. Pemeriksaan dijalankan ulang pada setiap INSERT dan UPDATE, tidak hanya pada saat tabel dibuat.
Apabila ekspresinya menghasilkan NULL karena kolom yang dirujuk belum terisi, baris tetap diterima, sebab NULL diperlakukan sama dengan hasil yang benar oleh constraint ini. Klausa tahun IS NULL OR pada contoh sengaja melonggarkan aturan untuk tahun yang belum diketahui, karena tanpa klausa tersebut setiap baris tanpa tahun akan ditolak. Baris kedua dengan tahun 2021 lolos pemeriksaan dan tersimpan seperti terlihat pada keluaran.
Perbedaan UNIQUE dan PRIMARY KEY#
Keduanya mencegah duplikasi, tetapi perannya berbeda. PRIMARY KEY menandai pengenal utama baris, sedangkan UNIQUE hanya menegakkan keunikan pada kolom biasa. Perbedaan praktisnya dirangkum pada tabel berikut.
| Aspek | PRIMARY KEY | UNIQUE |
|---|---|---|
| Peran | pengenal utama baris | mencegah duplikasi pada kolom biasa |
| Jumlah per tabel | satu | beberapa boleh |
Nilai NULL | tidak pernah tersimpan, nilai kosong diisi otomatis pada INTEGER PRIMARY KEY | diterima, bahkan pada beberapa baris |
Ada satu keanehan yang wajib diketahui. Menurut standar SQL, PRIMARY KEY seharusnya berarti NOT NULL, tetapi pada tabel biasa SQLite masih menerimanya untuk kolom yang bukan INTEGER PRIMARY KEY. Perilaku ini merupakan warisan versi lama yang dipertahankan demi kompatibilitas.
CREATE TABLE rak (
kode TEXT PRIMARY KEY,
lantai INTEGER NOT NULL
);
INSERT INTO rak (kode, lantai) VALUES (NULL, 1);
INSERT INTO rak (kode, lantai) VALUES (NULL, 2);
SELECT quote(kode), lantai FROM rak;NULL|1
NULL|2Kedua baris dengan kode bernilai NULL diterima, sehingga pengenal rak yang seharusnya unik kehilangan maknanya. Pada tabel strict dan tabel WITHOUT ROWID, keanehan ini sudah dihilangkan dan PRIMARY KEY menolak NULL.
Ringkasan#
INTEGER PRIMARY KEYmenjadi alias rowid dan mengisi nilainya secara otomatis bila dibiarkan kosong.NOT NULLdanCHECKmenolak baris yang melanggar aturan, sedangkanDEFAULTmengisi kolom yang ditinggalkan pada INSERT.UNIQUEmelarang duplikasi tetapi menerima beberapaNULL, dan PRIMARY KEY non-integer pada tabel biasa berperilaku sama karena perilaku lama yang dipertahankan.
Referensi#
- CREATE TABLE — SQLite — Definisi constraint kolom dan tabel beserta catatan perilaku NULL pada PRIMARY KEY.
- SQLite Query Language — SQLite — Indeks seluruh pernyataan SQL yang didukung SQLite.