Menu

NOT NULL dan DEFAULT di SQLite: Constraint Kolom

Cara kerja NOT NULL dan DEFAULT di SQLite: apa yang sebenarnya dijaga, trik CURRENT_TIMESTAMP, dan jebakan saat menambahkannya ke tabel yang sudah ada.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

Dua Constraint yang Sangat Berguna

Kebanyakan bug yang muncul dari skema yang asal-asalan bisa dilacak ke salah satu dari dua hal: kolom yang bernilai NULL padahal tidak ada yang menduganya, atau kolom yang tidak berisi nilai yang dianggap aplikasi pasti ada. NOT NULL dan DEFAULT menyelesaikan keduanya, dan hampir tidak ada biayanya untuk ditambahkan.

Satu kolom wajib diisi dan tidak punya nilai cadangan. Dua kolom punya nilai cadangan. Insert-nya hanya perlu memberikan email, dan SQLite mengisi sisanya. Itulah seluruh fiturnya dalam satu contoh. Sisa halaman ini membahas kasus-kasus pinggirannya.

NOT NULL Berarti "Tolak NULL, Tanpa Kecuali"

NOT NULL melakukan persis seperti namanya. Setiap upaya memasukkan NULL ke kolom itu, entah karena kolomnya tidak disebut di INSERT padahal tidak punya default, atau karena menulis NULL secara eksplisit, akan gagal:

Pesan error-nya:

Runtime error: NOT NULL constraint failed: posts.title

Hasilnya sama jika kamu memberikan NULL secara langsung:

INSERT INTO posts (id, title) VALUES (1, NULL);
-- Runtime error: NOT NULL constraint failed: posts.title

Itulah kontraknya. Jika sebuah kolom secara logika wajib ada, tandai dengan NOT NULL, dan kamu sudah menyingkirkan satu jenis bug sekaligus: tidak ada kode aplikasi yang bisa menyelundupkan NULL melewati database.

DEFAULT Memberi Nilai Saat Pemanggil Tidak Memberikannya

DEFAULT hanya berlaku saat sebuah INSERT sama sekali tidak menyebut kolom itu. Default tidak menyelamatkan NULL yang ditulis secara eksplisit:

Insert pertama mengandalkan default. Insert kedua menimpanya. Jika kamu menulis INSERT INTO tasks (title, status) VALUES ('x', NULL), kamu akan mendapat error NOT NULL constraint failed, karena kolomnya disebut sehingga default-nya tidak pernah dipakai.

Inilah model berpikir yang perlu kamu pegang: DEFAULT mengisi kolom yang tidak disebut. NOT NULL menolak null bagaimanapun datangnya. Keduanya fitur yang berdiri sendiri, dan cocok dipakai bersama.

Default Bisa Berupa Ekspresi

Default berupa literal adalah kasus yang umum (DEFAULT 0, DEFAULT '', DEFAULT 'pending'), tapi SQLite juga menerima ekspresi dalam tanda kurung. Dengan cara inilah kamu memberi cap waktu pembuatan pada baris, atau membuat ID acak:

Beberapa hal yang perlu kamu tahu:

  • Ekspresinya dihitung di setiap insert, bukan sekali saat tabel dibuat. Setiap baris mendapat timestamp dan token-nya sendiri.
  • CURRENT_TIMESTAMP, CURRENT_DATE, dan CURRENT_TIME adalah tiga kata kunci khusus yang tidak perlu tanda kurung. Selain itu, semuanya perlu.
  • Ekspresinya tidak boleh merujuk kolom lain atau subquery; ekspresi itu harus berdiri sendiri.

Jika kamu ingin kolom yang opsional tapi otomatis diberi cap waktu saat tersedia, buang NOT NULL dan pertahankan default-nya. Jika kamu ingin kolom itu wajib dan otomatis diberi cap waktu, pakai keduanya.

DEFAULT NULL Itu Sah (dan Kadang Memang Tujuannya)

Menulis DEFAULT NULL sama saja dengan tidak punya default sama sekali: kolomnya bernilai NULL saat kamu tidak memberi nilai. Ini layak dipakai jika kamu ingin menyatakan secara eksplisit di skema bahwa "tidak ada nilai" adalah keadaan awal yang memang diinginkan:

bio dan avatar berperilaku sama persis di sini. DEFAULT NULL pada bio adalah komentar dalam bentuk kode: memberi tahu siapa pun yang membaca skemanya bahwa tidak adanya bio adalah keadaan normal, bukan kelalaian.

Menambahkan NOT NULL ke Tabel yang Sudah Ada

Di sinilah urusannya jadi rumit. ALTER TABLE di SQLite sengaja dibatasi: kamu tidak bisa menjalankan ALTER COLUMN ... SET NOT NULL seperti di Postgres. Yang bisa kamu lakukan bergantung pada apakah kolomnya sudah ada atau belum.

Untuk kolom yang benar-benar baru, ADD COLUMN ... NOT NULL bisa dipakai, tapi kamu wajib memberikan default. Kalau tidak, baris yang sudah ada tiba-tiba akan berisi NULL di kolom NOT NULL, dan itu mustahil:

Coba hal yang sama tanpa default, dan kamu akan mendapat error:

ALTER TABLE products ADD COLUMN sku TEXT NOT NULL;
-- Runtime error: Cannot add a NOT NULL column with default value NULL

Untuk kolom yang sudah ada, tidak ada perubahan langsung di tempat. Resep standarnya adalah membangun ulang tabel: buat tabel baru dengan constraint yang kamu inginkan, salin datanya, hapus tabel lama, lalu ganti nama tabel baru. Kita akan membahasnya di halaman drop-and-alter-table. Untuk sekarang, cukup ketahui bahwa batasan ini nyata, dan rancang skemamu dengan mempertimbangkannya.

Kombinasi yang Realistis

Kebanyakan tabel produksi memakai kedua constraint sekaligus untuk menuangkan "apa yang diharapkan aplikasi selalu benar":

Baca skema itu dari atas ke bawah, dan kamu bisa menebak apa yang dilakukan aplikasinya tanpa melihat satu baris kode pun. customer wajib diisi dan tidak punya nilai cadangan: pemanggil harus tahu pesanan itu untuk siapa. Uang, mata uang, dan status semuanya punya default yang masuk akal, jadi insert yang paling sederhana pun tetap menghasilkan baris yang utuh. notes opsional. created_at diisi oleh database, satu-satunya tempat yang seharusnya mengisinya.

Itulah nilai constraint-constraint ini: mengubah asumsi menjadi aturan yang dijaga oleh database itu sendiri.

Kesalahan yang Sering Terjadi

Daftar singkat hal-hal yang sering menjebak:

  • NULL eksplisit mengalahkan DEFAULT. INSERT INTO t (col) VALUES (NULL) tidak akan memakai default. Kolomnya harus tidak ada di daftar kolom.
  • Default berupa ekspresi butuh tanda kurung. DEFAULT CURRENT_TIMESTAMP bisa dipakai (itu salah satu dari tiga kata kunci khusus). DEFAULT lower(hex(randomblob(8))) tidak bisa, jadi bungkus: DEFAULT (lower(hex(randomblob(8)))).
  • NOT NULL dan string kosong itu berbeda. '' adalah nilai TEXT yang sah dan tidak akan memicu constraint itu. Jika kamu juga ingin melarang string kosong, itu tugas CHECK (halaman berikutnya).
  • ADD COLUMN ... NOT NULL butuh DEFAULT yang bukan NULL. Tanpanya, SQLite menolak perubahan itu.

Selanjutnya: Constraint CHECK

NOT NULL dan DEFAULT mencakup "harus ada" dan "isi jika tidak ada." Untuk lapisan validasi berikutnya, seperti "harus positif", "harus salah satu dari nilai-nilai ini", atau "tanggal selesai harus setelah tanggal mulai", SQLite punya constraint CHECK, yang memungkinkanmu menulis ekspresi boolean apa pun yang harus dipenuhi setiap baris. Itulah topik halaman berikutnya.

Pertanyaan yang sering diajukan

Bagaimana cara membuat kolom wajib diisi di SQLite?

Tambahkan NOT NULL ke definisi kolomnya: email TEXT NOT NULL. Setiap INSERT atau UPDATE yang mencoba membiarkan kolom itu bernilai NULL akan gagal dengan NOT NULL constraint failed. Pasangkan dengan DEFAULT jika kamu ingin nilai cadangan saat pemanggil tidak memberikannya.

Bagaimana nilai default bekerja di SQLite?

DEFAULT <value> memberi kolom sebuah nilai yang dipakai saat INSERT tidak menentukannya. Default bisa berupa literal (DEFAULT 0, DEFAULT 'pending'), NULL, atau ekspresi dalam tanda kurung seperti DEFAULT (CURRENT_TIMESTAMP) atau DEFAULT (lower(hex(randomblob(8)))). Default berupa ekspresi dihitung ulang di setiap insert.

Kenapa SQLite menampilkan 'NOT NULL constraint failed' saat saya melakukan insert?

Kamu memasukkan baris tanpa memberi nilai untuk kolom NOT NULL yang tidak punya DEFAULT. Sertakan kolom itu di INSERT, beri kolom itu DEFAULT, atau longgarkan constraint-nya. Memberikan NULL secara eksplisit juga memicu error ini, karena NOT NULL menolak null dari mana pun asalnya.

Bisakah menambahkan NOT NULL ke kolom yang sudah ada di SQLite?

Tidak secara langsung, karena ALTER TABLE ... ALTER COLUMN tidak ada di SQLite. Kamu bisa menambahkan kolom baru dengan NOT NULL DEFAULT <value> (default-nya wajib untuk baris yang sudah ada), atau membangun ulang tabelnya: buat tabel baru dengan constraint itu, salin datanya, hapus tabel lama, lalu ganti nama tabel baru.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai