Semantik NULL

NULL bukan nol: logika tiga nilai, IS NULL, dan jebakan perbandingannya.

Daftar isi

Topik ini membahas NULL, yaitu penanda bahwa sebuah nilai tidak tersedia atau belum dicatat, beserta seluruh konsekuensinya pada penyaringan. Materinya mencakup logika tiga nilai, operator IS NULL dan IS NOT NULL, jebakan perbandingan yang membuang baris diam-diam, serta fungsi IFNULL dan COALESCE. Datasetnya tetap tabel buku perpustakaan, kali ini dengan satu stok yang sengaja dibiarkan kosong.

Stok yang belum dicatat#

Blok berikut membangun tabel buku dengan satu baris tambahan. Buku Suara Hujan tercatat di katalog, tetapi penghitungan stoknya belum dilakukan, sehingga kolom stoknya diisi NULL dan bukan angka.

sql Tabel buku dengan satu stok yang belum dicatat
CREATE TABLE buku (id INTEGER PRIMARY KEY, judul TEXT, pengarang TEXT, tahun INTEGER, stok INTEGER);
INSERT INTO buku VALUES
  (1, 'Dasar Algoritma', 'Ali', 2019, 4),
  (2, 'Peta Langit', 'Budi', 2021, 0),
  (3, 'Dasar Algoritma', 'Citra', 2021, 2),
  (4, 'Sejarah Kertas', 'Dewi', 2019, 3),
  (5, 'Kode dan Sandi', 'Eka', 2023, 5),
  (6, 'Jejak Angin', 'Farhan', 2020, 0),
  (7, 'Peta Langit', 'Budi', 2024, 7),
  (8, 'Suara Hujan', 'Dewi', 2018, NULL);
SELECT * FROM buku;
text Keluaran
1|Dasar Algoritma|Ali|2019|4
2|Peta Langit|Budi|2021|0
3|Dasar Algoritma|Citra|2021|2
4|Sejarah Kertas|Dewi|2019|3
5|Kode dan Sandi|Eka|2023|5
6|Jejak Angin|Farhan|2020|0
7|Peta Langit|Budi|2024|7
8|Suara Hujan|Dewi|2018|

Baris terakhir berakhir dengan tanda | yang diikuti kekosongan, karena sqlite3 menggambar NULL sebagai teks kosong pada modus keluarannya. Kekosongan itu berbeda dari angka 0 yang terlihat pada baris Peta Langit dan Jejak Angin. NULL menyatakan ketiadaan nilai, sedangkan 0 menyatakan nilai nol yang sungguh-sungguh dihitung. Pembedaan ini terasa sepele, tetapi menentukan hasil setiap query penyaringan pada bagian berikutnya.

Pada skema nyata, kolom yang boleh NULL adalah keputusan desain, bukan kebetulan. Kolom stok yang didefinisikan tanpa NOT NULL membolehkan baris seperti Suara Hujan ini masuk, dan seluruh query yang memakainya wajib mengakomodasi kekosongan itu sejak awal. Alternatifnya adalah menambahkan constraint NOT NULL agar baris yang belum lengkap ditolak saat penyisipan, tetapi pilihan tersebut membatasi data yang dapat ditampung, sehingga keduanya perlu ditimbang menurut kebutuhan aplikasi.

Logika tiga nilai#

Perbandingan biasa seperti stok > 0 selalu menghasilkan TRUE atau FALSE, tetapi perbandingan yang menyentuh NULL menghasilkan nilai ketiga bernama UNKNOWN. Aturan inilah yang disebut logika tiga nilai (three-valued logic), dan penyebabnya sederhana. Ketiadaan nilai tidak dapat dibandingkan dengan apa pun, sehingga SQLite menolak menyatakan hasilnya benar maupun salah. Untuk membuat nilai ketiga itu terlihat, query berikut memakai COALESCE sebagai penerjemah, yaitu fungsi yang mengembalikan argumen pertamanya yang bukan NULL.

sql Perbandingan biasa dan perbandingan dengan NULL
SELECT COALESCE(2 > 1, 'unknown');
SELECT COALESCE(NULL > 1, 'unknown');
SELECT COALESCE(NULL = NULL, 'unknown');
text Keluaran
1
unknown
unknown

Baris pertama menghasilkan 1 karena 2 > 1 bernilai TRUE, sehingga COALESCE meneruskannya. Dua baris berikutnya menghasilkan teks unknown, artinya ekspresi di dalamnya bernilai NULL. Angka 1 pada baris pertama adalah representasi TRUE yang dipakai SQLite, yaitu 1 untuk benar dan 0 untuk salah. Seluruh aturan ini berasal dari standar SQL, dan SQLite mengikutinya dengan setia. Perhatikan baris ketiga dengan saksama, karena NULL = NULL juga tidak bernilai TRUE. Dua kekosongan tidak dapat dinyatakan sama satu sama lain melalui operator =, dan fakta inilah yang menjadi akar seluruh jebakan NULL.

IS NULL dan IS NOT NULL#

Karena = tidak dapat menguji kekosongan, SQL menyediakan operator khusus. IS NULL menguji apakah sebuah nilai kosong, dan IS NOT NULL menguji sebaliknya. Keduanya tidak mengikuti logika tiga nilai, melainkan selalu menghasilkan TRUE atau FALSE secara murni. Jalankan query berikut untuk mengambil buku yang stoknya belum dicatat.

sql Mengambil baris berstok NULL
CREATE TABLE buku (id INTEGER PRIMARY KEY, judul TEXT, pengarang TEXT, tahun INTEGER, stok INTEGER);
INSERT INTO buku VALUES
  (1, 'Dasar Algoritma', 'Ali', 2019, 4),
  (2, 'Peta Langit', 'Budi', 2021, 0),
  (3, 'Dasar Algoritma', 'Citra', 2021, 2),
  (4, 'Sejarah Kertas', 'Dewi', 2019, 3),
  (5, 'Kode dan Sandi', 'Eka', 2023, 5),
  (6, 'Jejak Angin', 'Farhan', 2020, 0),
  (7, 'Peta Langit', 'Budi', 2024, 7),
  (8, 'Suara Hujan', 'Dewi', 2018, NULL);
SELECT judul, stok FROM buku WHERE stok IS NULL;
text Keluaran
Suara Hujan|

Hanya Suara Hujan yang lolos, dan stoknya tetap digambar kosong. Bentuk kebalikannya mengambil seluruh baris yang stoknya tercatat.

sql Mengambil baris yang stoknya tercatat
CREATE TABLE buku (id INTEGER PRIMARY KEY, judul TEXT, pengarang TEXT, tahun INTEGER, stok INTEGER);
INSERT INTO buku VALUES
  (1, 'Dasar Algoritma', 'Ali', 2019, 4),
  (2, 'Peta Langit', 'Budi', 2021, 0),
  (3, 'Dasar Algoritma', 'Citra', 2021, 2),
  (4, 'Sejarah Kertas', 'Dewi', 2019, 3),
  (5, 'Kode dan Sandi', 'Eka', 2023, 5),
  (6, 'Jejak Angin', 'Farhan', 2020, 0),
  (7, 'Peta Langit', 'Budi', 2024, 7),
  (8, 'Suara Hujan', 'Dewi', 2018, NULL);
SELECT judul, stok FROM buku WHERE stok IS NOT NULL;
text Keluaran
Dasar Algoritma|4
Peta Langit|0
Dasar Algoritma|2
Sejarah Kertas|3
Kode dan Sandi|5
Jejak Angin|0
Peta Langit|7

Tujuh baris yang tersisa terbagi dua habis dengan keluaran sebelumnya. Dua query inilah satu-satunya cara yang benar untuk menguji kekosongan pada SQL, dan keduanya berlaku untuk kolom teks maupun angka. Uji kekosongan juga berlaku pada ekspresi, sehingga hasil fungsi pun dapat diperiksa dengan IS NULL. Kedua operator ini dapat dipadukan dengan syarat lain memakai AND atau OR sebagaimana perbandingan biasa. Pada tabel yang lebih lebar, uji kekosongan sering dipasang pada beberapa kolom sekaligus untuk menemukan baris yang pencatatannya belum lengkap, dan tiap kolom memerlukan satu uji IS NULL tersendiri yang dihubungkan OR.

Jebakan penyaringan stok <> 0#

Jebakan NULL paling sering muncul pada query penyaringan yang tampaknya benar. Pertimbangkan permintaan berikut, yaitu daftar buku yang stoknya bukan nol, dan jawabannya ditulis sebagai WHERE stok <> 0.

sql Daftar buku berstok bukan nol
CREATE TABLE buku (id INTEGER PRIMARY KEY, judul TEXT, pengarang TEXT, tahun INTEGER, stok INTEGER);
INSERT INTO buku VALUES
  (1, 'Dasar Algoritma', 'Ali', 2019, 4),
  (2, 'Peta Langit', 'Budi', 2021, 0),
  (3, 'Dasar Algoritma', 'Citra', 2021, 2),
  (4, 'Sejarah Kertas', 'Dewi', 2019, 3),
  (5, 'Kode dan Sandi', 'Eka', 2023, 5),
  (6, 'Jejak Angin', 'Farhan', 2020, 0),
  (7, 'Peta Langit', 'Budi', 2024, 7),
  (8, 'Suara Hujan', 'Dewi', 2018, NULL);
SELECT id, judul, stok FROM buku WHERE stok <> 0;
text Keluaran
1|Dasar Algoritma|4
3|Dasar Algoritma|2
4|Sejarah Kertas|3
5|Kode dan Sandi|5
7|Peta Langit|7

Keluaran ini memuat lima baris, sedangkan Suara Hujan tidak ada padahal stoknya jelas bukan nol. Penyebabnya ada pada logika tiga nilai. Perbandingan NULL <> 0 menghasilkan UNKNOWN, dan WHERE hanya meneruskan baris yang kondisinya TRUE, sehingga baris NULL terbuang bersama baris berstok 0. Baris Peta Langit dan Jejak Angin memang sengaja dibuang, tetapi baris Suara Hujan hilang tanpa sengaja. Perbaikannya adalah menyatakan baris NULL secara eksplisit.

sql Penyaringan yang mempertahankan baris NULL
CREATE TABLE buku (id INTEGER PRIMARY KEY, judul TEXT, pengarang TEXT, tahun INTEGER, stok INTEGER);
INSERT INTO buku VALUES
  (1, 'Dasar Algoritma', 'Ali', 2019, 4),
  (2, 'Peta Langit', 'Budi', 2021, 0),
  (3, 'Dasar Algoritma', 'Citra', 2021, 2),
  (4, 'Sejarah Kertas', 'Dewi', 2019, 3),
  (5, 'Kode dan Sandi', 'Eka', 2023, 5),
  (6, 'Jejak Angin', 'Farhan', 2020, 0),
  (7, 'Peta Langit', 'Budi', 2024, 7),
  (8, 'Suara Hujan', 'Dewi', 2018, NULL);
SELECT id, judul, stok FROM buku WHERE stok <> 0 OR stok IS NULL;
text Keluaran
1|Dasar Algoritma|4
3|Dasar Algoritma|2
4|Sejarah Kertas|3
5|Kode dan Sandi|5
7|Peta Langit|7
8|Suara Hujan|

Enam baris kini tampil, karena stok IS NULL bernilai TRUE untuk Suara Hujan dan kata OR menghubungkannya dengan syarat utama. Bandingkan kedua keluaran itu untuk melihat baris yang sempat hilang.

IFNULL dan COALESCE#

Penyaringan bukan satu-satunya tempat NULL berperan, karena baris NULL juga ikut tampil pada hasil dan kadang perlu diganti nilainya. IFNULL menerima dua argumen dan mengembalikan argumen pertama bila nilainya bukan NULL, sedangkan argumen kedua dipakai sebagai pengganti bila kosong. Query berikut menampilkan seluruh stok dengan NULL diganti angka 0 pada tampilannya.

sql Mengganti NULL dengan nol pada hasil
CREATE TABLE buku (id INTEGER PRIMARY KEY, judul TEXT, pengarang TEXT, tahun INTEGER, stok INTEGER);
INSERT INTO buku VALUES
  (1, 'Dasar Algoritma', 'Ali', 2019, 4),
  (2, 'Peta Langit', 'Budi', 2021, 0),
  (3, 'Dasar Algoritma', 'Citra', 2021, 2),
  (4, 'Sejarah Kertas', 'Dewi', 2019, 3),
  (5, 'Kode dan Sandi', 'Eka', 2023, 5),
  (6, 'Jejak Angin', 'Farhan', 2020, 0),
  (7, 'Peta Langit', 'Budi', 2024, 7),
  (8, 'Suara Hujan', 'Dewi', 2018, NULL);
SELECT judul, IFNULL(stok, 0) AS stok_efektif FROM buku;
text Keluaran
Dasar Algoritma|4
Peta Langit|0
Dasar Algoritma|2
Sejarah Kertas|3
Kode dan Sandi|5
Jejak Angin|0
Peta Langit|7
Suara Hujan|0

Baris Suara Hujan kini menampilkan 0 tanpa mengubah isi tabelnya, karena penggantian itu hanya terjadi pada hasil query. Bentuk yang lebih umum adalah COALESCE, yang menerima banyak argumen dan mengembalikan yang pertama bukan NULL.

sql COALESCE dengan beberapa cadangan
SELECT COALESCE(NULL, NULL, 'nilai ketiga');
text Keluaran
nilai ketiga

Dua argumen pertama kosong, sehingga argumen ketiganya yang diambil. IFNULL sebenarnya adalah COALESCE dengan dua argumen, dan keduanya sering dipakai untuk menampilkan nilai cadangan pada laporan. Penggantian seperti ini juga berguna pada ekspresi lanjutan, misalnya perhitungan yang menghasilkan NULL bila salah satu operannya kosong, karena membungkus kolomnya dengan IFNULL membuat perhitungan tetap menghasilkan angka. Nama COALESCE lebih umum dijumpai pada query laporan, sedangkan IFNULL lebih ringkas untuk kasus satu cadangan, dan keduanya sama-sama mengembalikan hasil tanpa menyentuh tabel asalnya.

Kesalahan umum#

Kesalahan yang paling mendasar adalah menulis WHERE stok = NULL dengan harapan mengambil baris kosong. Query semacam itu selalu mengembalikan nol baris, karena perbandingannya bernilai UNKNOWN dan bukan TRUE. Query berikut menampilkan kedua bentuk pengujian itu berdampingan pada baris yang sama.

sql Perbandingan IS NULL dan = NULL
CREATE TABLE buku (id INTEGER PRIMARY KEY, judul TEXT, pengarang TEXT, tahun INTEGER, stok INTEGER);
INSERT INTO buku VALUES
  (1, 'Dasar Algoritma', 'Ali', 2019, 4),
  (2, 'Peta Langit', 'Budi', 2021, 0),
  (3, 'Dasar Algoritma', 'Citra', 2021, 2),
  (4, 'Sejarah Kertas', 'Dewi', 2019, 3),
  (5, 'Kode dan Sandi', 'Eka', 2023, 5),
  (6, 'Jejak Angin', 'Farhan', 2020, 0),
  (7, 'Peta Langit', 'Budi', 2024, 7),
  (8, 'Suara Hujan', 'Dewi', 2018, NULL);
SELECT judul, stok IS NULL, stok = NULL FROM buku WHERE stok IS NULL;
text Keluaran
Suara Hujan|1|

Kolom kedua berisi 1 karena stok IS NULL benar untuk baris ini, sedangkan kolom ketiga kosong karena stok = NULL menghasilkan NULL. Jika kolom ketiga itu dijadikan kondisi WHERE, tidak ada baris yang lolos, dan inilah kegagalan yang biasanya membingungkan. Perbaikannya selalu satu, yaitu memakai IS NULL atau IS NOT NULL. Pola yang keliru ini mudah terlihat saat peninjauan karena bentuknya khas, yaitu tanda sama dengan yang diikuti kata NULL, sehingga kebiasaan yang mencegahnya adalah memperlakukan penulisan tersebut sebagai kesalahan yang wajib diganti. Kesalahan kedua yang senada adalah mengisi nilai kosong dengan teks kosong '' lalu menganggapnya NULL, padahal keduanya berbeda dan IS NULL tidak akan menemukan teks kosong tersebut.

Ringkasan#

  • Perbandingan apa pun yang menyentuh NULL menghasilkan UNKNOWN, termasuk NULL = NULL, dan WHERE hanya meneruskan baris yang kondisinya TRUE.
  • IS NULL dan IS NOT NULL menguji kekosongan secara langsung dan selalu menghasilkan TRUE atau FALSE.
  • Penyaringan pada kolom yang boleh NULL wajib menambahkan IS NULL secara eksplisit agar barisnya tidak terbuang diam-diam.
  • IFNULL dan COALESCE mengganti NULL dengan nilai cadangan pada hasil query tanpa mengubah data yang tersimpan.

Referensi#