Kondisi tunggal jarang cukup untuk kebutuhan nyata. Laporan stok Toko Maju Jaya butuh “harga di atas dua puluh ribu dan masih aktif”; halaman katalog butuh “kategori kertas atau elektronik”; pengecualian butuh “bukan yang sudah tidak aktif”. Operator logika AND, OR, dan NOT menggabungkan kondisi-kondisi itu menjadi satu kesatuan. Ada satu hal yang membedakan logika SQL dari logika pemrograman biasa: boolean SQL punya tiga nilai — true, false, dan NULL — sehingga tabel kebenarannya juga tiga dimensi. Pada chapter ini kita membedah logika tiga nilai itu sampai tuntas, karena tabel kebenarannya menjelaskan hampir semua kejutan filtering yang dibahas di section ini.
Menyiapkan Tabel Latihan #
Tabel latihan kita tetap produk milik Toko Maju Jaya — delapan baris yang sama dengan chapter sebelumnya, dibuat ulang di bundle chapter ini supaya semua contoh berdiri sendiri. Kolom promo yang berisi NULL pada empat baris kembali menjadi bahan utama demo logika tiga nilai:
CREATE TABLE produk (
id integer PRIMARY KEY,
nama text NOT NULL,
kategori text NOT NULL,
harga numeric(10, 2) NOT NULL,
stok integer NOT NULL,
aktif boolean NOT NULL,
tanggal_masuk date,
promo text
);
INSERT INTO produk (id, nama, kategori, harga, stok, aktif, tanggal_masuk, promo) VALUES
(1, 'Buku Tulis 58', 'alat tulis', 4500, 120, true, DATE '2025-11-10', NULL),
(2, 'Pulpen Hitam', 'alat tulis', 3000, 200, true, DATE '2025-12-01', 'Diskon 10%'),
(3, 'Kertas HVS A4', 'kertas', 55000, 40, true, DATE '2026-01-05', 'Diskon 50%'),
(4, 'Stapler Besar', 'alat tulis', 24000, 15, false, DATE '2026-01-20', NULL),
(5, 'Amplop Coklat', 'kertas', 1500, 500, true, DATE '2026-02-14', NULL),
(6, 'Penghapus', 'alat tulis', 2000, 0, false, DATE '2026-02-14', NULL),
(7, 'Kalkulator Solar', 'elektronik', 185000, 8, true, DATE '2026-03-02', 'Diskon 25%'),
(8, 'Penggaris 30cm', 'alat tulis', 3500, 85, true, DATE '2026-03-15', 'Diskon 5%');Output yang Dihasilkan:
CREATE TABLE
INSERT 0 8Tiga Operator Logika untuk Dua Nilai #
Mari mulai dari perilaku yang sudah akrab: menggabungkan true dan false tanpa NULL. AND meloloskan hasil true hanya bila kedua operannya true; OR cukup dengan satu operan true; NOT membalik nilai. Kita bisa menguji semuanya langsung lewat SELECT tanpa FROM — PostgreSQL mengevaluasi ekspresi boolean seperti kalkulator:
SELECT true AND true AS "benar AND benar",
true AND false AS "benar AND salah",
false OR false AS "salah OR salah",
false OR true AS "salah OR benar",
NOT true AS "NOT benar",
NOT false AS "NOT salah";Output yang Dihasilkan:
benar AND benar | benar AND salah | salah OR salah | salah OR benar | NOT benar | NOT salah
-----------------+-----------------+----------------+----------------+-----------+-----------
t | f | f | t | f | t
(1 row)Enam hasil itu membentuk tabel kebenaran klasik dua nilai. Dengan fondasi itu di tangan, kita sekarang bisa melangkah ke wilayah yang khas SQL.
Logika Tiga Nilai: NULL Berubah Menjawab #
Masalah muncul ketika salah satu operan bernilai NULL. Ingat aturan dari chapter Operator Perbandingan: perbandingan yang menyentuh NULL menghasilkan NULL. Ekspresi promo = 'Diskon 50%' bernilai NULL untuk empat produk yang promonya kosong. Pertanyaannya: NULL AND true itu apa? NULL OR false itu apa? Mari kita tanya langsung ke PostgreSQL:
SELECT NULL AND true AS "null AND benar",
NULL AND false AS "null AND salah",
NULL OR true AS "null OR benar",
NULL OR false AS "null OR salah",
NOT NULL AS "NOT null";
SELECT count(*) AS baris_dari_where_null
FROM produk
WHERE NULL;Output yang Dihasilkan:
null AND benar | null AND salah | null OR benar | null OR salah | NOT null
----------------+----------------+---------------+---------------+----------
| f | t | |
(1 row)
baris_dari_where_null
-----------------------
0
(1 row)Hasilnya memperlihatkan logika tiga nilai (three-valued logic) yang bekerja dengan prinsip “jawablah hanya bila pasti”:
NULL AND true→ NULL. Bila operan pertama ternyatafalse, hasil pastifalse; bilatrue, hasil mengikuti operan kedua. Karena NULL bisa jadi keduanya, jawaban jujurnya “belum bisa dipastikan”.NULL AND false→ false. Operan kedua saja sudah cukup memastikanfalse—ANDbutuh kedua operantrue, dan satufalsemengunci hasilnya. NULL tidak lagi berpengaruh.NULL OR true→ true. Sama ke balik arah: satu operantruesudah cukup memenangkanOR, apa pun nilai operan lainnya.NULL OR false→ NULL danNOT NULL→ NULL. Kedua ekspresi ini benar-benar tergantung nilai NULL-nya, jadi hasilnya ikut tidak pasti.
Tabel kebenaran lengkapnya ringkas seperti ini. AND:
a AND b | b = true | b = false | b = NULL |
|---|---|---|---|
a = true | true | false | NULL |
a = false | false | false | false |
a = NULL | NULL | false | NULL |
OR:
a OR b | b = true | b = false | b = NULL |
|---|---|---|---|
a = true | true | true | true |
a = false | true | false | NULL |
a = NULL | true | NULL | NULL |
NOT membalik dua nilai pasti dan membiarkan NULL tetap NULL: NOT true → false, NOT false → true, NOT NULL → NULL.
Konsekuensi terakhir dari prinsip “hanya true yang lolos” terlihat pada query kedua di file tadi: WHERE NULL sah secara sintaks, mengembalikan nol baris, tanpa error. Baris hanya lolos bila kondisinya benar-benar true — false dan NULL sama-sama kandas. Inilah mesin di balik jebakan baris NULL yang hilang diam-diam di chapter WHERE.
Menggabungkan Kondisi di WHERE #
Sekarang kita terapkan ketiganya pada tabel produk. Tiga pola paling umum: syarat ganda dengan AND, pilihan kategori dengan OR, dan pembalikan dengan NOT:
SELECT id, nama, harga, aktif FROM produk
WHERE harga > 20000 AND aktif = true
ORDER BY id;
SELECT id, nama, kategori FROM produk
WHERE kategori = 'kertas' OR kategori = 'elektronik'
ORDER BY id;
SELECT id, nama, harga FROM produk
WHERE NOT (harga > 20000)
ORDER BY id;Output yang Dihasilkan:
id | nama | harga | aktif
----+------------------+-----------+-------
3 | Kertas HVS A4 | 55000.00 | t
7 | Kalkulator Solar | 185000.00 | t
(2 rows)
id | nama | kategori
----+------------------+------------
3 | Kertas HVS A4 | kertas
5 | Amplop Coklat | kertas
7 | Kalkulator Solar | elektronik
(3 rows)
id | nama | harga
----+----------------+---------
1 | Buku Tulis 58 | 4500.00
2 | Pulpen Hitam | 3000.00
5 | Amplop Coklat | 1500.00
6 | Penghapus | 2000.00
8 | Penggaris 30cm | 3500.00
(5 rows)Mari kita bedah ketiganya:
harga > 20000 AND aktif = truemenjaring dua produk:Kertas HVS A4danKalkulator Solar.Stapler Besar(harga 24000, tetapiaktiffalse) gugur di setengah jalan — syarat pertama lolos, syarat kedua tidak,true AND false=false.kategori = 'kertas' OR kategori = 'elektronik'menjaring tiga produk dari gabungan dua kategori.ORtidak peduli dari kategori mana baris berasal, cukup satu syarat terpenuhi.NOT (harga > 20000)membalik perbandingan: lima produk berkolesterol harga rendah lolos, tiga yang mahal tersaring. Hasilnya identik denganharga <= 20000— dua bentuk itu ekuivalen untuk nilai non-NULL.
IS TRUE, IS FALSE, IS UNKNOWN #
Kadang kita perlu menguji nilai kebenaran sebuah ekspresi itu sendiri, bukan memakainya sebagai filter. PostgreSQL menyediakan IS TRUE, IS FALSE, dan IS UNKNOWN untuk itu — operasi yang mengembalikan true/false pasti, tidak pernah NULL. Ini adalah jembatan keluar dari logika tiga nilai:
SELECT (NULL AND true) IS TRUE AS is_true,
(NULL AND true) IS FALSE AS is_false,
(NULL AND true) IS UNKNOWN AS is_unknown;
SELECT id, nama, promo FROM produk
WHERE (promo = 'Diskon 50%') IS UNKNOWN
ORDER BY id;Output yang Dihasilkan:
is_true | is_false | is_unknown
---------+----------+------------
f | f | t
(1 row)
id | nama | promo
----+---------------+-------
1 | Buku Tulis 58 |
4 | Stapler Besar |
5 | Amplop Coklat |
6 | Penghapus |
(4 rows)Baris pertama memastikan bahwa NULL AND true tidak true (f) dan tidak false (f), melainkan UNKNOWN (t). Baris-baris di bawahnya adalah pemanfaatan praktisnya: kondisi (promo = 'Diskon 50%') IS UNKNOWN menjaring tepat empat produk yang promo-nya NULL — produk yang tidak bisa dijawab “ya” maupun “tidak” oleh pertanyaan apakah promonya Diskon 50%. Varian IS NOT TRUE (dibahas di chapter boolean pada Section Tipe Data) bahkan memasukkan baris NULL tersebut — perilaku yang sering jadi jawaban atas jebakan != yang hilang diam-diam.
Kurung untuk Kendali dan Keterbacaan #
Ketika AND dan OR bercampur dalam satu kondisi, urutan evaluasinya tidak lagi sekadar “kiri ke kanan”: AND dievaluasi lebih dulu daripada OR, sama seperti perkalian mendahului penjumlahan di aritmetika. Tanda kurung menimpa aturan itu sekaligus membuat maksud query terlihat telanjang. Perbandingan dua penempatan kurung berikut memperlihatkan bedanya:
SELECT id, nama, kategori, harga FROM produk
WHERE (kategori = 'kertas' OR kategori = 'elektronik') AND harga > 20000
ORDER BY id;
SELECT id, nama, kategori, harga FROM produk
WHERE kategori = 'kertas' OR (kategori = 'elektronik' AND harga > 20000)
ORDER BY id;Output yang Dihasilkan:
id | nama | kategori | harga
----+------------------+------------+-----------
3 | Kertas HVS A4 | kertas | 55000.00
7 | Kalkulator Solar | elektronik | 185000.00
(2 rows)
id | nama | kategori | harga
----+------------------+------------+-----------
3 | Kertas HVS A4 | kertas | 55000.00
5 | Amplop Coklat | kertas | 1500.00
7 | Kalkulator Solar | elektronik | 185000.00
(3 rows)Query pertama mencari “produk kertas atau elektronik yang harganya di atas 20000” — dua produk lolos (Kertas HVS A4 dan Kalkulator Solar). Query kedua berbeda total: tiga produk lolos, karena Amplop Coklat (kertas, harga 1500) ikut — syarat kategori = 'kertas' sendirian sudah memenangkan OR tanpa peduli harga. Kedua query sah secara sintaks, tidak ada error, tetapi menjawab pertanyaan yang berbeda.
Logika tiga nilai dan peran kurung kini sudah di tangan. Chapter berikutnya membedah persis titik di mana dua hal itu bertabrakan: jebakan precedence AND/OR yang membuat query tanpa kurung mengembalikan baris yang tidak kita minta, plus pola penulisan ulang OR menjadi IN yang membuat kondisi berantai jadi ringkas.
Referensi #
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
buat-tabel.sql
Unduh berkasCREATE TABLE produk (
id integer PRIMARY KEY,
nama text NOT NULL,
kategori text NOT NULL,
harga numeric(10, 2) NOT NULL,
stok integer NOT NULL,
aktif boolean NOT NULL,
tanggal_masuk date,
promo text
);
INSERT INTO produk (id, nama, kategori, harga, stok, aktif, tanggal_masuk, promo) VALUES
(1, 'Buku Tulis 58', 'alat tulis', 4500, 120, true, DATE '2025-11-10', NULL),
(2, 'Pulpen Hitam', 'alat tulis', 3000, 200, true, DATE '2025-12-01', 'Diskon 10%'),
(3, 'Kertas HVS A4', 'kertas', 55000, 40, true, DATE '2026-01-05', 'Diskon 50%'),
(4, 'Stapler Besar', 'alat tulis', 24000, 15, false, DATE '2026-01-20', NULL),
(5, 'Amplop Coklat', 'kertas', 1500, 500, true, DATE '2026-02-14', NULL),
(6, 'Penghapus', 'alat tulis', 2000, 0, false, DATE '2026-02-14', NULL),
(7, 'Kalkulator Solar', 'elektronik', 185000, 8, true, DATE '2026-03-02', 'Diskon 25%'),
(8, 'Penggaris 30cm', 'alat tulis', 3500, 85, true, DATE '2026-03-15', 'Diskon 5%');
is-true-false-unknown.sql
Unduh berkasSELECT (NULL AND true) IS TRUE AS is_true,
(NULL AND true) IS FALSE AS is_false,
(NULL AND true) IS UNKNOWN AS is_unknown;
SELECT id, nama, promo FROM produk
WHERE (promo = 'Diskon 50%') IS UNKNOWN
ORDER BY id;
kombinasi-kondisi.sql
Unduh berkasSELECT id, nama, harga, aktif FROM produk
WHERE harga > 20000 AND aktif = true
ORDER BY id;
SELECT id, nama, kategori FROM produk
WHERE kategori = 'kertas' OR kategori = 'elektronik'
ORDER BY id;
SELECT id, nama, harga FROM produk
WHERE NOT (harga > 20000)
ORDER BY id;
kurung.sql
Unduh berkasSELECT id, nama, kategori, harga FROM produk
WHERE (kategori = 'kertas' OR kategori = 'elektronik') AND harga > 20000
ORDER BY id;
SELECT id, nama, kategori, harga FROM produk
WHERE kategori = 'kertas' OR (kategori = 'elektronik' AND harga > 20000)
ORDER BY id;
logika-dasar.sql
Unduh berkasSELECT true AND true AS "benar AND benar",
true AND false AS "benar AND salah",
false OR false AS "salah OR salah",
false OR true AS "salah OR benar",
NOT true AS "NOT benar",
NOT false AS "NOT salah";
logika-null.sql
Unduh berkasSELECT NULL AND true AS "null AND benar",
NULL AND false AS "null AND salah",
NULL OR true AS "null OR benar",
NULL OR false AS "null OR salah",
NOT NULL AS "NOT null";
SELECT count(*) AS baris_dari_where_null
FROM produk
WHERE NULL;