NULL di SQL: Kenapa Bikin Hasil Query Kamu Salah Diam-diam
TL;DR
NULL di SQL adalah penanda bahwa nilai sebuah kolom nggak diketahui atau nggak ada, bukan nol dan bukan string kosong. Perbandingan apa pun dengan NULL menghasilkan UNKNOWN, bukan TRUE atau FALSE — makanya WHERE kolom = NULL selalu balik kosong. Buat ngecek NULL, pakai IS NULL atau IS NOT NULL.
NULL di SQL adalah penanda bahwa nilai sebuah kolom nggak diketahui. Bukan nol. Bukan string kosong. Bukan spasi.
Kedengeran sepele. Tapi salah paham soal NULL itu penyebab paling umum kenapa laporan kamu ngasih angka yang beda sama data mentah — tanpa satu pun pesan error muncul.
Query-nya jalan. Hasilnya keluar. Angkanya salah.
Apa itu NULL di SQL?
NULL adalah penanda khusus yang artinya "nilai di sini nggak ada atau nggak diketahui". Dia bukan tipe data dan bukan nilai — dia justru ketiadaan nilai.
Bedanya sama yang mirip-mirip:
| Nilai | Artinya | Contoh kasus |
|---|---|---|
| NULL | Kita nggak tau | Pelanggan belum isi kolom email |
| 0 | Angka nol beneran | Pelanggan belanja Rp0 bulan ini |
| '' (string kosong) | Teks tanpa karakter | Kolom catatan sengaja dikosongin |
| ' ' (spasi) | Teks berisi 1 spasi | Data kotor hasil import |
Contoh di artikel ini pakai tabel pelanggan dari dataset Toko Berkah — 5 baris, beberapa kolomnya sengaja NULL.
CREATE TABLE pelanggan (
id INT PRIMARY KEY,
nama VARCHAR(50),
email VARCHAR(80),
telepon VARCHAR(20),
kota VARCHAR(30),
total_belanja INT
);
INSERT INTO pelanggan VALUES
(1, 'Siti Rahayu', 'siti@mail.com', '081234567890', 'Jakarta', 4500000),
(2, 'Budi Santoso', NULL, '081298765432', 'Bandung', 2300000),
(3, 'Dewi Lestari', 'dewi@mail.com', NULL, 'Jakarta', NULL),
(4, 'Agus Pratama', NULL, NULL, 'Surabaya', 890000),
(5, 'Rina Wulandari', 'rina@mail.com', '081377788899', NULL, 6200000);
Kenapa WHERE kolom = NULL selalu kosong?
Coba jalanin ini:
SELECT * FROM pelanggan WHERE email = NULL;
Hasilnya: 0 baris. Padahal jelas-jelas ada 2 pelanggan yang emailnya NULL.
Penyebabnya, SQL pakai logika tiga nilai: TRUE, FALSE, dan UNKNOWN. Perbandingan apa pun dengan NULL menghasilkan UNKNOWN — termasuk NULL = NULL.
Klausa WHERE cuma ngeloloskan baris yang hasilnya TRUE. UNKNOWN dibuang, sama kayak FALSE.
Cara yang benar:
SELECT nama, kota FROM pelanggan WHERE email IS NULL;
-- Budi Santoso, Agus Pratama
SELECT nama, email FROM pelanggan WHERE email IS NOT NULL;
-- Siti, Dewi, Rina
IS NULL dan IS NOT NULL. Itu satu-satunya cara ngecek NULL di SQL.
Gimana NULL bikin COUNT kamu salah?
Ini jebakan yang paling sering bikin laporan nggak cocok.
SELECT
COUNT(*) AS semua_baris,
COUNT(email) AS punya_email,
COUNT(telepon) AS punya_telepon
FROM pelanggan;
Hasilnya: 5, 3, 3.
COUNT(*) ngitung baris. COUNT(email) ngitung nilai yang bukan NULL di kolom email.
Jadi kalau bos nanya "berapa pelanggan kita?", jawabannya 5. Kalau dia nanya "berapa yang bisa kita email?", jawabannya 3. Dua angka berbeda dari tabel yang sama.
Aku pernah lihat tim marketing salah hitung conversion rate gara-gara ini. Pembilangnya pakai COUNT(kolom), penyebutnya pakai COUNT(*). Angkanya kelihatan wajar, jadi nggak ada yang curiga selama 3 bulan.
Gimana NULL bikin AVG dan SUM salah?
Fungsi agregat ngelewatin NULL. Bukan nganggapnya nol — dia bener-bener nggak ngitung barisnya.
SELECT
SUM(total_belanja) AS total,
COUNT(total_belanja) AS jumlah_isi,
AVG(total_belanja) AS rata_rata
FROM pelanggan;
Total belanja: Rp13.890.000. Baris yang keitung: 4, bukan 5. Jadi AVG-nya Rp3.472.500 — hasil bagi 4.
Kalau kamu ngira NULL dianggap nol, kamu bakal ngira rata-ratanya Rp2.778.000 (dibagi 5). Selisihnya Rp694.500 per pelanggan. Di dataset 10.000 baris, selisih segini bisa jadi laporan yang salah total.
Kalau NULL memang harus dianggap nol, bilang ke database secara eksplisit:
SELECT AVG(COALESCE(total_belanja, 0)) FROM pelanggan;
Detail cara kerjanya ada di artikel COALESCE SQL.
Kenapa aritmetika sama NULL hasilnya selalu NULL?
SELECT nama, total_belanja + 100000 AS setelah_bonus FROM pelanggan;
Dewi Lestari punya total_belanja NULL. Hasil NULL + 100000 bukan 100000 — tapi NULL.
Logikanya masuk akal kalau dipikir: kalau kita nggak tau berapa belanjanya, kita juga nggak tau berapa hasilnya setelah ditambah bonus.
Efeknya nyebar. Satu kolom NULL di tengah rumus panjang bikin seluruh hasilnya NULL. Ini yang bikin kolom perhitungan kamu tiba-tiba kosong padahal rumusnya bener.
Jebakan NULL yang paling sering makan korban
1. NOT IN ketemu NULL
Ini yang paling ganas.
SELECT nama FROM pelanggan
WHERE kota NOT IN (SELECT kota FROM pelanggan WHERE total_belanja > 5000000);
Subquery-nya narik kota Rina, yang nilainya NULL. Begitu ada NULL di dalam daftar NOT IN, seluruh kondisi jadi UNKNOWN dan hasilnya kosong total.
Nol baris. Bukan error. Cuma kosong.
Cara amannya, pakai NOT EXISTS:
SELECT p.nama FROM pelanggan p
WHERE NOT EXISTS (
SELECT 1 FROM pelanggan x
WHERE x.total_belanja > 5000000 AND x.kota = p.kota
);
2. LEFT JOIN yang tiba-tiba jadi INNER JOIN
Kalau kamu naruh kondisi kolom tabel kanan di WHERE (bukan di ON), baris NULL hasil LEFT JOIN bakal kebuang. LEFT JOIN kamu diam-diam berubah jadi INNER JOIN.
-- LEFT JOIN yang "rusak"
SELECT p.nama, t.id
FROM pelanggan p
LEFT JOIN transaksi t ON t.pelanggan_id = p.id
WHERE t.status = 'lunas'; -- baris NULL kebuang di sini
-- Yang benar: pindahin ke ON
SELECT p.nama, t.id
FROM pelanggan p
LEFT JOIN transaksi t ON t.pelanggan_id = p.id AND t.status = 'lunas';
3. NULL di GROUP BY
GROUP BY nggabungin semua NULL jadi satu grup. Jadi kamu bakal lihat satu baris hasil dengan kolom grup kosong. Ini beda sama WHERE, yang justru buang NULL.
4. UNIQUE constraint dan NULL
Di kebanyakan database, kolom dengan UNIQUE constraint tetap boleh punya banyak baris NULL. Soalnya dua NULL nggak dianggap sama. Jadi UNIQUE nggak nge-block duplikat NULL.
5. ORDER BY dan posisi NULL
PostgreSQL naruh NULL di akhir waktu ORDER BY ASC. MySQL naruh di awal. Kalau kamu pindah database dan urutan laporannya berubah, ini penyebabnya. Pakai NULLS FIRST atau NULLS LAST biar eksplisit.
Gimana cara nangani NULL dengan aman?
- Cek pakai IS NULL, bukan = NULL. Nggak ada pengecualian.
- Ganti NULL sebelum dihitung. Pakai COALESCE kalau NULL memang harus dianggap nol atau nilai default lain.
- Hindari NOT IN kalau subquery-nya bisa ngasih NULL. Pakai NOT EXISTS.
- Taruh kondisi tabel kanan di ON, bukan WHERE, waktu pakai LEFT JOIN.
- Cegah dari awal. Pasang NOT NULL constraint di kolom yang memang nggak boleh kosong. Lebih baik nolak data kotor di pintu masuk daripada bersihin di query.
Aturan logika tiga nilai ini standar SQL, bukan kebiasaan satu database. Kamu bisa baca definisi resminya di dokumentasi comparison functions PostgreSQL.
FAQ
Apa bedanya NULL, nol, dan string kosong di SQL?
NULL artinya nilainya nggak diketahui atau nggak ada. Nol (0) itu angka beneran yang nilainya nol. String kosong ('') itu teks beneran yang panjangnya nol karakter. Ketiganya beda di database. Pelanggan dengan total_belanja NULL artinya kita nggak tau berapa dia belanja. Yang total_belanja-nya 0 artinya kita tau dia belanja nol rupiah.
Kenapa WHERE kolom = NULL selalu kosong?
Karena perbandingan apa pun dengan NULL menghasilkan UNKNOWN, bukan TRUE. Klausa WHERE cuma ngeloloskan baris yang hasilnya TRUE, jadi baris NULL selalu dibuang — bahkan waktu kamu justru lagi nyari NULL. Solusinya pakai IS NULL.
Apakah COUNT menghitung NULL?
COUNT(*) ngitung semua baris termasuk yang isinya NULL. COUNT(nama_kolom) cuma ngitung baris yang kolomnya bukan NULL. Di tabel 5 baris dengan 2 email kosong, COUNT(*) ngasih 5 dan COUNT(email) ngasih 3. Beda 2 angka ini yang sering bikin laporan nggak cocok sama data mentah.
Gimana AVG dan SUM memperlakukan NULL?
Keduanya ngelewatin baris NULL, bukan nganggapnya nol. Kalau 5 pelanggan tapi 1 total_belanja-nya NULL, AVG bakal ngebagi total dengan 4, bukan 5. Rata-ratanya jadi lebih tinggi dari yang kamu kira. Kalau NULL memang harus dianggap nol, bungkus dulu pakai COALESCE(total_belanja, 0).
Kenapa NOT IN dengan NULL hasilnya kosong?
Kalau daftar di dalam NOT IN mengandung satu saja NULL, seluruh kondisi jadi UNKNOWN dan nggak ada baris yang lolos. Ini terjadi diam-diam waktu subquery kamu narik kolom yang boleh NULL. Cara amannya: tambahin WHERE kolom IS NOT NULL di subquery, atau ganti seluruhnya pakai NOT EXISTS.
Penutup
Yang perlu kamu inget soal NULL:
- NULL artinya "nggak diketahui", bukan nol. Perbandingan sama NULL hasilnya UNKNOWN.
- COUNT(*) dan COUNT(kolom) ngasih angka beda kalau ada NULL. Pilih yang sesuai pertanyaan bisnisnya.
- NOT IN dan LEFT JOIN adalah dua tempat NULL paling sering bikin bug tanpa error.
Cara paling cepat ngerti NULL? Bikin tabel 5 baris kayak contoh di atas, kosongin beberapa sel, terus jalanin COUNT(*) dan COUNT(kolom) berdampingan. Lihat angkanya beda. Setelah itu kamu nggak akan lupa.
Lanjut baca soal cara ngeganti NULL dengan nilai default di COALESCE SQL, atau pelajari operator perbandingannya di halaman fungsi WHERE NgulikData.
Mau praktek langsung? Mulai latihan SQL gratis
Latihan interaktif, langsung di browser.
Artikel Terkait
Take-Home Test SQL Data Analyst: Contoh Soal + Cara Ngerjainnya
Contoh soal take-home test SQL yang beneran dipakai perusahaan, dikerjain step by step — dari baca soal, nulis query, sampai nyusun insight yang bikin recruiter nengok.
Latihan Soal SQL Menengah: Window Function & CTE
8 soal SQL level menengah pakai window function dan CTE, lengkap dengan jawaban, penjelasan, dan jebakan yang sering bikin orang salah.
50 Latihan Soal SQL Pemula + Kunci Jawaban
50 latihan soal SQL dari SELECT sampai window function, pakai dataset toko_berkah. Kunci jawabannya ada semua, tapi coba dulu sendiri sebelum ngintip.