Chapter SELECT
sudah memperlihatkan cara mengambil kolom yang kita butuhkan, tetapi hasilnya selalu memuat semua baris tabel. Laporan stok menipis hanya butuh produk dengan stok di bawah ambang tertentu; halaman katalog hanya menampilkan produk yang masih aktif; rekap kuartal hanya menyentuh transaksi tiga bulan terakhir. Klausa WHERE adalah alat yang memotong hasil query menjadi baris-baris yang memenuhi syarat saja — fondasi hampir semua query yang berguna. Pada chapter ini kita membangun intuisi WHERE dari contoh teks, angka, tanggal, sampai satu jebakan NULL yang sering membuat hasil query “kehilangan” baris secara diam-diam.
Menyiapkan Tabel Latihan #
Seluruh Section Filtering dan Sorting memakai satu domain yang sama: tabel produk milik Toko Maju Jaya dengan delapan baris tetap. Delapan baris itu cukup beragam — tiga kategori, harga dari Rp1.500 sampai Rp185.000, dua produk tidak aktif, satu stok habis, dan empat baris tanpa promo (NULL) — sehingga setiap bab berikutnya bisa memakai data yang sama tanpa berganti konteks:
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 8Dua kolom sengaja dibuat boleh NULL: tanggal_masuk dan promo. Nilai NULL di dua kolom itu akan menjadi bahan demo penting — bukan hanya di chapter ini, tetapi juga di bab Operator Logika dan IN dan NOT IN.
Cara Kerja Klausa WHERE #
Mekanisme WHERE sederhana namun perlu dipahami betul: PostgreSQL mengevaluasi kondisi di dalam WHERE untuk setiap baris satu per satu. Baris yang kondisinya bernilai true ikut ke hasil; baris yang bernilai false dibuang. Kondisi tersebut adalah ekspresi boolean — biasanya perbandingan antara kolom dan nilai. Bentuk paling dasarnya menyamakan kolom teks dengan sebuah nilai:
SELECT id, nama, kategori FROM produk
WHERE kategori = 'kertas'
ORDER BY id;
SELECT id, nama, harga FROM produk
WHERE harga > 20000
ORDER BY id;
SELECT id, nama, stok FROM produk
WHERE stok < 50
ORDER BY id;
SELECT id, nama, aktif FROM produk
WHERE aktif = false
ORDER BY id;Output yang Dihasilkan:
id | nama | kategori
----+---------------+----------
3 | Kertas HVS A4 | kertas
5 | Amplop Coklat | kertas
(2 rows)
id | nama | harga
----+------------------+-----------
3 | Kertas HVS A4 | 55000.00
4 | Stapler Besar | 24000.00
7 | Kalkulator Solar | 185000.00
(3 rows)
id | nama | stok
----+------------------+------
3 | Kertas HVS A4 | 40
4 | Stapler Besar | 15
6 | Penghapus | 0
7 | Kalkulator Solar | 8
(4 rows)
id | nama | aktif
----+---------------+-------
4 | Stapler Besar | f
6 | Penghapus | f
(2 rows)Empat perintah di atas memperlihatkan pola yang sama pada tiga jenis data berbeda:
WHERE kategori = 'kertas'menyaring berdasarkan teks — hanyaKertas HVS A4danAmplop Coklatyang lolos.WHERE harga > 20000danWHERE stok < 50menyaring berdasarkan angka — perbandingan numerik mengikuti urutan nilai, bukan urutan baris.WHERE aktif = falsemenyaring berdasarkan boolean — barisStapler BesardanPenghapusyang sudah tidak aktif tertangkap semua.
Perhatikan juga bahwa ORDER BY id tetap dipasang di setiap perintah. Filtering dan pengurutan adalah dua urusan yang berbeda: WHERE menentukan baris mana yang lolos, ORDER BY menentukan urutan tampilnya. Pembahasan urutan lengkap ada di chapter ORDER BY.
Menyaring Berdasarkan Tanggal #
Kolom tanggal_masuk bertipe date, dan perbandingannya memakai literal tanggal tetap dengan awalan DATE. Literal DATE '2026-01-01' ditulis dalam format ISO (YYYY-MM-DD) — format yang tidak ambigu dan sebaiknya menjadi kebiasaan:
SELECT id, nama, tanggal_masuk FROM produk
WHERE tanggal_masuk >= DATE '2026-01-01'
ORDER BY id;
SELECT id, nama, tanggal_masuk FROM produk
WHERE tanggal_masuk = DATE '2026-02-14'
ORDER BY id;Output yang Dihasilkan:
id | nama | tanggal_masuk
----+------------------+---------------
3 | Kertas HVS A4 | 2026-01-05
4 | Stapler Besar | 2026-01-20
5 | Amplop Coklat | 2026-02-14
6 | Penghapus | 2026-02-14
7 | Kalkulator Solar | 2026-03-02
8 | Penggaris 30cm | 2026-03-15
(6 rows)
id | nama | tanggal_masuk
----+---------------+---------------
5 | Amplop Coklat | 2026-02-14
6 | Penghapus | 2026-02-14
(2 rows)Perintah pertama mengembalikan enam produk yang masuk sejak awal 2026 — dua produk dari 2025 (Buku Tulis 58 dan Pulpen Hitam) tersaring keluar. Perintah kedua memperlihatkan pencarian tanggal persis: dua baris dengan tanggal_masuk tepat 14 Februari 2026, bukan hanya satu, karena dua produk memang masuk di hari yang sama. Literal untuk timestamp mengikuti pola yang sama, misalnya TIMESTAMP '2026-02-14 08:30:00', dan dibahas bersama operator perbandingan lengkap di chapter berikutnya.
Jebakan NULL yang Hilang Diam-diam #
Inilah bagian yang membuat banyak pemula kehilangan tidur: WHERE hanya meloloskan baris yang kondisinya benar-benar true. Bila kondisi bernilai NULL — “tidak diketahui” — baris dibuang seolah tidak memenuhi syarat, tanpa pesan kesalahan apa pun. Kolom promo yang boleh NULL menjadi contoh sempurna. Coba tebak dulu: dari delapan produk, berapa yang promonya bukan Diskon 50%?
SELECT count(*) AS total_produk FROM produk;
SELECT id, nama, promo FROM produk
WHERE promo != 'Diskon 50%'
ORDER BY id;
SELECT count(*) AS promo_kosong FROM produk
WHERE promo IS NULL;Output yang Dihasilkan:
total_produk
--------------
8
(1 row)
id | nama | promo
----+------------------+------------
2 | Pulpen Hitam | Diskon 10%
7 | Kalkulator Solar | Diskon 25%
8 | Penggaris 30cm | Diskon 5%
(3 rows)
promo_kosong
--------------
4
(1 row)Jawaban intuitif kita berkata tujuh: delapan produk dikurangi satu yang promonya Diskon 50%. Kenyataannya hanya tiga baris yang muncul. Empat produk lainnya punya promo NULL, dan NULL != 'Diskon 50%' tidak bernilai false melainkan NULL — “tidak bisa dipastikan”. Baris-baris itu pun dibuang secara diam-diam, tanpa error, tanpa peringatan. Perbandingan 8 = 3 + 4 + 1 menutup pembukuan: tiga baris lolos, empat baris NULL hilang, satu baris Diskon 50% tersaring memang.
WHERE pada UPDATE dan DELETE #
Klausa WHERE bukan milik SELECT saja — ia yang membatai baris mana yang diubah UPDATE dan baris mana yang dihapus DELETE. Dua skenario kecil di Toko Maju Jaya memperlihatkannya. Pertama, stok Penghapus tercatat 0 padahal gudang masih menyimpan sepuluh unit — stok dikoreksi dengan UPDATE yang hanya menyentuh baris bersyarat. Kedua, semua produk yang sudah tidak aktif ditarik dari katalog dengan DELETE bersyarat:
UPDATE produk SET stok = stok + 10
WHERE stok = 0;
SELECT id, nama, stok FROM produk
WHERE id = 6;
DELETE FROM produk
WHERE aktif = false;
SELECT id, nama, stok, aktif FROM produk
ORDER BY id;Output yang Dihasilkan:
UPDATE 1
id | nama | stok
----+-----------+------
6 | Penghapus | 10
(1 row)
DELETE 2
id | nama | stok | aktif
----+------------------+------+-------
1 | Buku Tulis 58 | 120 | t
2 | Pulpen Hitam | 200 | t
3 | Kertas HVS A4 | 40 | t
5 | Amplop Coklat | 500 | t
7 | Kalkulator Solar | 8 | t
8 | Penggaris 30cm | 85 | t
(6 rows)Tag UPDATE 1 menunjukkan tepat satu baris berubah — baris dengan stok = 0 hanya ada satu, yaitu Penghapus (0 menjadi 10). Tag DELETE 2 menghapus dua baris tidak aktif (Stapler dan Penghapus), dan listing akhir hanya menyisakan enam produk aktif. Detail kedua perintah ini sudah dibahas di Section Manipulasi Data; yang perlu diingat di sini satu hal saja — tanpa WHERE, UPDATE dan DELETE menyapu seluruh tabel.
Kondisi WHERE Harus Bernilai Boolean #
Satu kesalahan yang kerap muncul saat belajar: menaruh ekspresi non-boolean langsung setelah WHERE, misalnya angka. PostgreSQL menolaknya saat parsing:
SELECT id, nama FROM produk WHERE 1 ORDER BY id;Output yang Dihasilkan:
psql:where-non-boolean.sql:1: ERROR: argument of WHERE must be type boolean, not type integer
LINE 1: SELECT id, nama FROM produk WHERE 1 ORDER BY id;
^Pesan argument of WHERE must be type boolean, not type integer menegaskan aturannya: apapun yang ditaruh setelah WHERE harus menghasilkan nilai boolean (true/false/NULL) untuk setiap baris. Angka 1, nama kolom harga, atau string 'kertas' tidak lolos — semuanya harus dibandingkan lebih dulu (harga > 0, nama = 'kertas') agar menjadi boolean.
Dengan WHERE kita bisa menyaring baris, tetapi kondisi kategori = 'kertas' di atas masih memakai satu operator saja. PostgreSQL menyediakan tujuh operator perbandingan — dari = sampai >= — dengan perilaku yang berbeda untuk angka, teks, dan tanggal; itu semua materi chapter Operator Perbandingan.
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%');
filter-dasar.sql
Unduh berkasSELECT id, nama, kategori FROM produk
WHERE kategori = 'kertas'
ORDER BY id;
SELECT id, nama, harga FROM produk
WHERE harga > 20000
ORDER BY id;
SELECT id, nama, stok FROM produk
WHERE stok < 50
ORDER BY id;
SELECT id, nama, aktif FROM produk
WHERE aktif = false
ORDER BY id;
filter-tanggal.sql
Unduh berkasSELECT id, nama, tanggal_masuk FROM produk
WHERE tanggal_masuk >= DATE '2026-01-01'
ORDER BY id;
SELECT id, nama, tanggal_masuk FROM produk
WHERE tanggal_masuk = DATE '2026-02-14'
ORDER BY id;
null-tersembunyi.sql
Unduh berkasSELECT count(*) AS total_produk FROM produk;
SELECT id, nama, promo FROM produk
WHERE promo != 'Diskon 50%'
ORDER BY id;
SELECT count(*) AS promo_kosong FROM produk
WHERE promo IS NULL;
ubah-hapus.sql
Unduh berkasUPDATE produk SET stok = stok + 10
WHERE stok = 0;
SELECT id, nama, stok FROM produk
WHERE id = 6;
DELETE FROM produk
WHERE aktif = false;
SELECT id, nama, stok, aktif FROM produk
ORDER BY id;
where-non-boolean.sql
Unduh berkasSELECT id, nama FROM produk WHERE 1 ORDER BY id;