Kolom yang boleh kosong menyimpan NULL — nilai yang berarti “tidak diketahui” atau “belum terisi”, bukan nol dan bukan string kosong. Sederhana kedengarannya, tetapi NULL menjalar ke seluruh query dengan aturan sendiri: NULL + 5 bukan 5, NULL = NULL bukan benar, dan COUNT bisa menghitung dua jawaban berbeda untuk tabel yang sama. Pada chapter ini kita bedah perilaku NULL memakai tabel pelanggan yang kolom telepon dan email-nya sengaja dibiarkan berlubang, lalu kita kenalkan tiga fungsi pengendali NULL: IFNULL, COALESCE, dan NULLIF.

Menyiapkan Tabel Latihan #

sql
DROP DATABASE IF EXISTS belajar_fungsi;
CREATE DATABASE belajar_fungsi;
USE belajar_fungsi;

Output yang Dihasilkan:

text
Query OK, 0 rows affected, 1 warning (0.00 sec)

Query OK, 1 row affected (0.00 sec)

Database changed
sql
CREATE TABLE pelanggan (
  id INT AUTO_INCREMENT PRIMARY KEY,
  nama VARCHAR(40) NOT NULL,
  telepon VARCHAR(20) NULL,
  email VARCHAR(60) NULL,
  kota VARCHAR(30) NOT NULL
);

INSERT INTO pelanggan (nama, telepon, email, kota) VALUES
  ('Budi', '0812345678', '[email protected]', 'Bandung'),
  ('Siti', NULL, '[email protected]', 'Bandung'),
  ('Rina', '0898765432', NULL, 'Surabaya'),
  ('Tono', NULL, NULL, 'Yogyakarta'),
  ('Dewi', '0812999888', '[email protected]', 'Surabaya'),
  ('Agus', NULL, '[email protected]', 'Medan');

Output yang Dihasilkan:

text
Query OK, 0 rows affected (0.00 sec)

Query OK, 6 rows affected (0.00 sec)
Records: 6  Duplicates: 0  Warnings: 0

Dari enam pelanggan, tiga orang tidak mencantumkan telepon dan dua di antaranya tidak punya email tercatat. Baris Tono menjadi kasus paling menarik: kedua kolom kontaknya NULL, sehingga query apa pun yang mengandalkan kontak akan kesulitan bila tidak menyiapkan nilai pengganti.

NULL Bukan Nol dan Bukan String Kosong #

Operasi apa pun yang menyentuh NULL akan menghasilkan NULL — aturan ini disebut propagasi NULL:

sql
SELECT NULL + 5 AS hitung,
       CONCAT('halo ', NULL) AS teks,
       UPPER(NULL) AS huruf_besar,
       NULL = NULL AS banding_sama,
       NULL = '' AS banding_kosong;

Output yang Dihasilkan:

text
+--------+------+--------------------------+--------------+----------------+
| hitung | teks | huruf_besar              | banding_sama | banding_kosong |
+--------+------+--------------------------+--------------+----------------+
|   NULL | NULL | NULL                     |         NULL |           NULL |
+--------+------+--------------------------+--------------+----------------+
1 row in set (0.00 sec)

Kolom hitung bernilai NULL, bukan 5: “tidak diketahui ditambah lima” tetap tidak diketahui. Kolom banding_sama juga NULL, bukan 1 — dan inilah perilaku paling menjebak: perbandingan dengan NULL tidak menghasilkan benar atau salah, melainkan tidak diketahui pula. Untuk membedakan NULL dari string kosong, kita bisa membandingkan hasil fungsi:

sql
SELECT LENGTH(NULL) AS panjang_null, LENGTH('') AS panjang_kosong;

Output yang Dihasilkan:

text
+--------------+----------------+
| panjang_null | panjang_kosong |
+--------------+----------------+
|         NULL |              0 |
+--------------+----------------+
1 row in set (0.00 sec)

String kosong punya panjang 0 dan bisa dihitung, sedangkan NULL punya panjang NULL karena tidak ada nilai untuk dihitung. Perbedaan mendasar ini penting saat membaca data: baris “telepon belum diisi” (NULL) dan “telepon dikosongkan sengaja” (string kosong) membawa makna yang berbeda.

Membandingkan NULL dengan IS NULL #

Operator IS NULL dan IS NOT NULL adalah satu-satunya cara yang benar untuk menguji keberadaan NULL:

sql
SELECT nama, kota FROM pelanggan
WHERE telepon IS NULL
ORDER BY id;

Output yang Dihasilkan:

text
+------+------------+
| nama | kota       |
+------+------------+
| Siti | Bandung    |
| Tono | Yogyakarta |
| Agus | Medan      |
+------+------------+
3 rows in set (0.00 sec)

Tiga pelanggan tanpa telepon tampil lengkap. Kebalikannya, IS NOT NULL menyaring baris yang kolomnya terisi:

sql
SELECT nama, telepon FROM pelanggan
WHERE email IS NOT NULL
ORDER BY id;

Output yang Dihasilkan:

text
+------+------------+
| nama | telepon    |
+------+------------+
| Budi | 0812345678 |
| Siti | NULL       |
| Dewi | 0812999888 |
| Agus | NULL       |
+------+------------+
4 rows in set (0.00 sec)

Mengganti NULL dengan IFNULL #

IFNULL(nilai, pengganti) mengembalikan nilai bila tidak NULL, dan pengganti bila NULL:

sql
SELECT nama, telepon, IFNULL(telepon, 'belum ada nomor') AS kontak
FROM pelanggan
ORDER BY id;

Output yang Dihasilkan:

text
+------+------------+-----------------+
| nama | telepon    | kontak          |
+------+------------+-----------------+
| Budi | 0812345678 | 0812345678      |
| Siti | NULL       | belum ada nomor |
| Rina | 0898765432 | 0898765432      |
| Tono | NULL       | belum ada nomor |
| Dewi | 0812999888 | 0812999888      |
| Agus | NULL       | belum ada nomor |
+------+------------+-----------------+
6 rows in set (0.00 sec)

Fungsi ini tepat untuk satu kolom dengan satu nilai pengganti. Tetapi bagaimana bila kita punya beberapa kandidat kontak — telepon, lalu email, lalu teks default? Membuat IFNULL bersarang (IFNULL(telepon, IFNULL(email, '...'))) membuat query cepat sulit dibaca. Untuk itu ada COALESCE.

COALESCE: Rantai Nilai Pengganti #

COALESCE(a, b, c, ...) mengembalikan argumen pertama yang tidak NULL — argumen sisanya tidak dievaluasi hasilnya:

sql
SELECT nama,
       COALESCE(telepon, email, 'belum ada kontak') AS kontak_utama
FROM pelanggan
ORDER BY id;

Output yang Dihasilkan:

text
+------+------------------+
| nama | kontak_utama     |
+------+------------------+
| Budi | 0812345678       |
| Siti | [email protected] |
| Rina | 0898765432       |
| Tono | belum ada kontak |
| Dewi | 0812999888       |
| Agus | [email protected] |
+------+------------------+
6 rows in set (0.00 sec)

Pelanggan dengan telepon menampilkan nomornya; Siti dan Agus jatuh ke email; Tono yang keduanya NULL menerima teks belum ada kontak. Deretan argumen dibaca seperti daftar prioritas dari kiri ke kanan, dan COALESCE boleh menerima dua argumen saja (setara IFNULL) sampai sebanyak yang kita butuhkan.

NULLIF: Membuat NULL secara Sengaja #

NULLIF(a, b) bekerja ke arah sebaliknya: menghasilkan NULL bila a = b, dan a bila berbeda. Kegunaan klasiknya adalah mengamankan pembagian dari pembagi nol:

sql
SELECT NULLIF(5, 5) AS sama_jadi_null,
       NULLIF(5, 7) AS beda_kembali_nilai,
       100 / NULLIF(0, 0) AS pembagian_aman;

Output yang Dihasilkan:

text
+----------------+--------------------+----------------+
| sama_jadi_null | beda_kembali_nilai | pembagian_aman |
+----------------+--------------------+----------------+
|           NULL |                  5 |           NULL |
+----------------+--------------------+----------------+
1 row in set (0.00 sec)

Ekspresi NULLIF(0, 0) menghasilkan NULL, sehingga 100 / NULL menghasilkan NULL — bukan error pembagian nol. Pola pembagi_aman = NULLIF(kolom, 0) sangat sering dipakai pada query laporan rata-rata atau persentase yang pembaginya bisa nol.

COUNT Bintang vs COUNT Kolom #

Fungsi agregat COUNT punya dua bentuk dengan perilaku berbeda terhadap NULL:

sql
SELECT COUNT(*) AS semua_baris,
       COUNT(telepon) AS baris_bernomor,
       COUNT(email) AS baris_beremail
FROM pelanggan;

Output yang Dihasilkan:

text
+-------------+----------------+----------------+
| semua_baris | baris_bernomor | baris_beremail |
+-------------+----------------+----------------+
|           6 |              3 |              4 |
+-------------+----------------+----------------+
1 row in set (0.00 sec)

COUNT(*) menghitung baris apa adanya (6), sedangkan COUNT(telepon) hanya menghitung baris yang kolom telepon-nya tidak NULL (3). Aturan yang sama berlaku untuk SUM, AVG, MIN, dan MAX: nilai NULL diabaikan dari perhitungan. Kita akan menyelami akibatnya — termasuk kejutan SUM yang bisa menghasilkan NULL — di chapter Fungsi Agregat dan section Agregasi Data.

Membersihkan Database Latihan #

sql
DROP DATABASE belajar_fungsi;

Output yang Dihasilkan:

text
Query OK, 1 row affected (0.00 sec)

NULL kini bukan lagi kotak hitam: kita bisa mengujinya dengan IS NULL, menggantinya dengan IFNULL dan COALESCE, bahkan menciptakannya dengan sengaja lewat NULLIF. Bab berikutnya melangkah lebih jauh — mengambil keputusan di dalam query dengan CASE WHEN dan IF(), termasuk pola klasiknya: mengubah kode status menjadi label berbahasa Indonesia yang enak dibaca.

Referensi #