Aturan bisnis hampir selalu bersyarat: pelanggan dengan stok nol ditandai “habis”, harga diskon berbeda antara minuman dan bahan pokok, dan laporan mingguan ingin barang kritis tampil paling atas. Di bahasa pemrograman biasa, logika seperti itu hidup di if/else — tetapi query SQL harus menghasilkan nilai dalam satu ekspresi. PostgreSQL menjawabnya dengan CASE WHEN ... THEN ... ELSE ... END, konstruksi bersyarat standar SQL yang bisa dipakai di hampir semua tempat yang menerima nilai: daftar SELECT, ORDER BY, WHERE, sampai UPDATE. Chapter ini membedah dua bentuk CASE, menunjukkan posisi-posisi pemakaiannya, dan menutup dengan pola CASE di dalam fungsi agregat — jembatan menuju section Agregasi Data.
Tabel Latihan #
Lima produk berikut cukup untuk mewakili semua skenario: ada stok habis, stok menipis, dan tiga kategori yang berbeda:
CREATE TABLE produk (
id integer PRIMARY KEY,
nama text NOT NULL,
kategori text NOT NULL,
stok integer NOT NULL,
harga numeric(12,2) NOT NULL
);
INSERT INTO produk (id, nama, kategori, stok, harga) VALUES
(1, 'Kopi Gayo', 'minuman', 0, 85000.00),
(2, 'Teh Melati', 'minuman', 4, 35000.00),
(3, 'Gula Merah', 'bahan pokok', 25, 15000.00),
(4, 'Beras Pandan Wangi', 'bahan pokok', 12, 72000.00),
(5, 'Susu Kedelai 1 Liter', 'minuman', 6, 28000.00);
SELECT id, nama, kategori, stok, harga
FROM produk
ORDER BY id;Output yang Dihasilkan:
CREATE TABLE
INSERT 0 5
id | nama | kategori | stok | harga
----+----------------------+-------------+------+----------
1 | Kopi Gayo | minuman | 0 | 85000.00
2 | Teh Melati | minuman | 4 | 35000.00
3 | Gula Merah | bahan pokok | 25 | 15000.00
4 | Beras Pandan Wangi | bahan pokok | 12 | 72000.00
5 | Susu Kedelai 1 Liter | minuman | 6 | 28000.00
(5 rows)Kolom yang akan kita perlakukan adalah stok (0 untuk Kopi Gayo, 4 untuk Teh Melati) dan kategori (minuman versus bahan pokok). Semua contoh di bawah mengambil keputusan dari kedua kolom itu.
Bentuk Pencarian: CASE WHEN Kondisi THEN Hasil #
Bentuk pertama dan paling lentur disebut searched CASE: setiap cabang membawa kondisi lengkapnya sendiri, dan cabang pertama yang bernilai true menang. Struktur umumnya CASE WHEN kondisi THEN hasil ... ELSE hasil_default END:
-- bentuk pencarian (searched CASE): kondisi bebas di tiap cabang
SELECT nama, stok,
CASE
WHEN stok = 0 THEN 'habis'
WHEN stok <= 5 THEN 'menipis'
ELSE 'aman'
END AS status_stok
FROM produk
ORDER BY id;
-- tanpa ELSE: baris yang tidak lolos kondisi apa pun menghasilkan NULL
SELECT nama, stok,
CASE WHEN stok = 0 THEN 'habis' END AS label_habis
FROM produk
ORDER BY id;
-- CASE bisa mengembalikan angka, bukan hanya teks
SELECT nama, harga,
CASE
WHEN stok = 0 THEN 0
WHEN stok <= 5 THEN 10
ELSE 5
END AS diskon_persen
FROM produk
ORDER BY id;Output yang Dihasilkan:
nama | stok | status_stok
----------------------+------+-------------
Kopi Gayo | 0 | habis
Teh Melati | 4 | menipis
Gula Merah | 25 | aman
Beras Pandan Wangi | 12 | aman
Susu Kedelai 1 Liter | 6 | aman
(5 rows)
nama | stok | label_habis
----------------------+------+-------------
Kopi Gayo | 0 | habis
Teh Melati | 4 |
Gula Merah | 25 |
Beras Pandan Wangi | 12 |
Susu Kedelai 1 Liter | 6 |
(5 rows)
nama | harga | diskon_persen
----------------------+----------+---------------
Kopi Gayo | 85000.00 | 0
Teh Melati | 35000.00 | 10
Gula Merah | 15000.00 | 5
Beras Pandan Wangi | 72000.00 | 5
Susu Kedelai 1 Liter | 28000.00 | 5
(5 rows)Mari kita bedah perilaku-perilaku kuncinya:
- Cabang dievaluasi berurutan dari atas: Kopi Gayo dengan
stok = 0berhenti di cabang pertama dan berlabel'habis'; Teh Melati denganstok = 4lolos cabang pertama (kondisi salah) lalu tertangkap cabang kedua'menipis'. Urutan cabang adalah urutan prioritas — tukar dua cabang pertama dan hasilnya berubah. - Tanpa ELSE, hasilnya NULL: query kedua hanya memberi label pada stok nol; keempat produk lainnya menampilkan kolom
label_habisyang kosong. Perilaku ini kembali ke hukum NULL dari chapter Fungsi NULL — jika NULL tidak diinginkan, selalu tulis cabangELSE, atau bungkus denganCOALESCE. - Hasil cabang boleh angka: kolom
diskon_persenmengembalikan0,10, atau5— nilai yang langsung bisa dihitung, misalnyaharga * (100 - diskon_persen) / 100.0. Satu aturan tegas: seluruh cabang harus menghasilkan tipe yang kompatibel — campuran'habis'dan10dalam satu CASE akan ditolak server.
Bentuk Sederhana: CASE Ekspresi WHEN Nilai #
Bentuk kedua, simple CASE, membandingkan satu ekspresi terhadap daftar nilai — lebih ringkas ketika kondisinya memang perbandingan kesamaan:
-- bentuk sederhana (simple CASE): satu ekspresi dibandingkan ke daftar nilai
SELECT nama, kategori,
CASE kategori
WHEN 'minuman' THEN 'rak dingin'
WHEN 'bahan pokok' THEN 'rak utama'
ELSE 'rak umum'
END AS lokasi_rak
FROM produk
ORDER BY id;
-- NULLIF adalah CASE yang dibekukan menjadi fungsi
SELECT NULLIF('sama', 'sama') AS nullif_langsung,
CASE WHEN 'sama' = 'sama' THEN NULL ELSE 'sama' END AS case_padanan;
SELECT NULLIF('beda', 'sama') AS nullif_beda,
CASE WHEN 'beda' = 'sama' THEN NULL ELSE 'beda' END AS case_beda;Output yang Dihasilkan:
nama | kategori | lokasi_rak
----------------------+-------------+------------
Kopi Gayo | minuman | rak dingin
Teh Melati | minuman | rak dingin
Gula Merah | bahan pokok | rak utama
Beras Pandan Wangi | bahan pokok | rak utama
Susu Kedelai 1 Liter | minuman | rak dingin
(5 rows)
nullif_langsung | case_padanan
-----------------+--------------
|
(1 row)
nullif_beda | case_beda
-------------+-----------
beda | beda
(1 row)Perbedaan kedua bentuk terletak pada posisi ekspresi: pada bentuk sederhana, kategori ditulis sekali setelah kata CASE, lalu tiap cabang hanya menyebut nilai pembandingnya — WHEN 'minuman' berarti kategori = 'minuman'. Gunakan bentuk pencarian saat kondisi beragam (stok = 0, stok <= 5), dan bentuk sederhana saat semua kondisi adalah kesamaan terhadap satu kolom. Dua query berikutnya membuktikan hubungan kekerabatan yang menarik: NULLIF('sama', 'sama') menghasilkan nilai yang persis sama dengan CASE WHEN 'sama' = 'sama' THEN NULL ELSE 'sama' END — NULLIF memang CASE dua cabang yang dibekukan menjadi fungsi. Sebaliknya, fungsi GREATEST dan LEAST yang dibahas di chapter Fungsi NULL
bisa dipandang sebagai CASE pilihan nilai ekstrem: GREATEST(a, b) setara memilih a jika a >= b, selain b.
CASE di ORDER BY, WHERE, dan UPDATE #
Kekuatan CASE yang sesungguhnya muncul ketika ia keluar dari daftar SELECT. Tiga posisi berikut adalah yang paling sering dipakai di dunia nyata:
-- CASE di ORDER BY: urutan khusus yang bukan urutan alfabetis
SELECT nama, kategori
FROM produk
ORDER BY CASE kategori
WHEN 'bahan pokok' THEN 1
WHEN 'minuman' THEN 2
ELSE 3
END,
nama;
-- CASE di WHERE: saring baris berdasarkan label hasil CASE
SELECT nama, stok,
CASE WHEN stok <= 5 THEN 'perlu restok' ELSE 'cukup' END AS status_restok
FROM produk
WHERE CASE WHEN stok <= 5 THEN 'perlu restok' ELSE 'cukup' END = 'perlu restok'
ORDER BY nama;
-- CASE di UPDATE: aturan harga berbeda per kategori dalam satu pernyataan
UPDATE produk
SET harga = CASE kategori
WHEN 'minuman' THEN round(harga * 0.90, 2)
ELSE round(harga * 0.95, 2)
END;
SELECT nama, kategori, harga
FROM produk
ORDER BY id;Output yang Dihasilkan:
nama | kategori
----------------------+-------------
Beras Pandan Wangi | bahan pokok
Gula Merah | bahan pokok
Kopi Gayo | minuman
Susu Kedelai 1 Liter | minuman
Teh Melati | minuman
(5 rows)
nama | stok | status_restok
------------+------+---------------
Kopi Gayo | 0 | perlu restok
Teh Melati | 4 | perlu restok
(2 rows)
UPDATE 5
nama | kategori | harga
----------------------+-------------+----------
Kopi Gayo | minuman | 76500.00
Teh Melati | minuman | 31500.00
Gula Merah | bahan pokok | 14250.00
Beras Pandan Wangi | bahan pokok | 68400.00
Susu Kedelai 1 Liter | minuman | 25200.00
(5 rows)ORDER BY CASE: bahan pokok diberi kunci1, minuman2— daftar hasil terurut sesuai prioritas bisnis, bukan abjad. Tie-breaknamadi belakangnya menjaga urutan tetap stabil di dalam tiap kelompok.WHERE CASE ... = 'perlu restok': secara teknis sah — CASE menghasilkan teks, teks dibandingkan, baris tersaring. Secara gaya, ini biasanya keputusan yang buruk: kondisistok <= 5ditulis langsung di WHERE akan jauh lebih jelas dan lebih mudah dioptimalkan server. Pemakaian yang sah muncul saat kondisi disusun dinamis dari data lain.UPDATE ... SET harga = CASE ... END: inilah pemakaianCASEyang paling menghemat kerja — satu pernyataan UPDATE menurunkan harga minuman 10% (85000.00menjadi76500.00) dan selain minuman 5% (72000.00menjadi68400.00). Tanpa CASE, Anda perlu dua UPDATE dengan dua WHERE yang saling melengkapi — dan dua kali pekerjaan pembacaan tabel.
CASE di Dalam Agregat: Pratinjau #
Satu posisi lagi menutup chapter ini dan sekaligus membuka pintu ke section berikutnya: CASE di dalam fungsi agregat. Pola count(CASE WHEN kondisi THEN 1 END) menghitung baris per kondisi dalam satu kali pembacaan tabel:
-- pola pivot mini: CASE di dalam agregat menghitung per kategori sekaligus
SELECT count(*) AS total_produk,
count(CASE WHEN stok = 0 THEN 1 END) AS produk_habis,
count(CASE WHEN stok > 0 AND stok <= 5 THEN 1 END) AS produk_menipis,
count(CASE WHEN kategori = 'minuman' THEN 1 END) AS produk_minuman
FROM produk;Output yang Dihasilkan:
total_produk | produk_habis | produk_menipis | produk_minuman
--------------+--------------+----------------+----------------
5 | 1 | 1 | 3
(1 row)Satu baris hasil menjawab empat pertanyaan sekaligus: lima produk total, satu habis (Kopi Gayo), satu menipis (Teh Melati), dan tiga minuman. Trik di balik pola ini adalah kolusi dua sifat yang sudah Anda kuasai: cabang CASE tanpa ELSE menghasilkan NULL untuk baris yang tidak lolos, dan count(ekspresi) hanya menghitung nilai non-NULL — sehingga baris yang tidak relevan otomatis hilang dari hitungan. Perluasan pola ini ke SUM(CASE WHEN ...), GROUP BY, dan laporan pivot-baris menjadi fokus utama section Agregasi Data
, yang dimulai dari chapter COUNT
.
Sebelum melangkah ke sana, ada satu keluarga fungsi yang belum sempat kita jamah dalam section ini padahal dipakai di hampir setiap laporan: count, sum, avg, min, max. Chapter berikutnya memberikan tur kilat sebelum pembahasan mendalamnya di section berikutnya: Fungsi Agregat
.
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,
stok integer NOT NULL,
harga numeric(12,2) NOT NULL
);
INSERT INTO produk (id, nama, kategori, stok, harga) VALUES
(1, 'Kopi Gayo', 'minuman', 0, 85000.00),
(2, 'Teh Melati', 'minuman', 4, 35000.00),
(3, 'Gula Merah', 'bahan pokok', 25, 15000.00),
(4, 'Beras Pandan Wangi', 'bahan pokok', 12, 72000.00),
(5, 'Susu Kedelai 1 Liter', 'minuman', 6, 28000.00);
SELECT id, nama, kategori, stok, harga
FROM produk
ORDER BY id;
case-agregat.sql
Unduh berkas-- pola pivot mini: CASE di dalam agregat menghitung per kategori sekaligus
SELECT count(*) AS total_produk,
count(CASE WHEN stok = 0 THEN 1 END) AS produk_habis,
count(CASE WHEN stok > 0 AND stok <= 5 THEN 1 END) AS produk_menipis,
count(CASE WHEN kategori = 'minuman' THEN 1 END) AS produk_minuman
FROM produk;
case-di-klausa.sql
Unduh berkas-- CASE di ORDER BY: urutan khusus yang bukan urutan alfabetis
SELECT nama, kategori
FROM produk
ORDER BY CASE kategori
WHEN 'bahan pokok' THEN 1
WHEN 'minuman' THEN 2
ELSE 3
END,
nama;
-- CASE di WHERE: saring baris berdasarkan label hasil CASE
SELECT nama, stok,
CASE WHEN stok <= 5 THEN 'perlu restok' ELSE 'cukup' END AS status_restok
FROM produk
WHERE CASE WHEN stok <= 5 THEN 'perlu restok' ELSE 'cukup' END = 'perlu restok'
ORDER BY nama;
-- CASE di UPDATE: aturan harga berbeda per kategori dalam satu pernyataan
UPDATE produk
SET harga = CASE kategori
WHEN 'minuman' THEN round(harga * 0.90, 2)
ELSE round(harga * 0.95, 2)
END;
SELECT nama, kategori, harga
FROM produk
ORDER BY id;
case-pencarian.sql
Unduh berkas-- bentuk pencarian (searched CASE): kondisi bebas di tiap cabang
SELECT nama, stok,
CASE
WHEN stok = 0 THEN 'habis'
WHEN stok <= 5 THEN 'menipis'
ELSE 'aman'
END AS status_stok
FROM produk
ORDER BY id;
-- tanpa ELSE: baris yang tidak lolos kondisi apa pun menghasilkan NULL
SELECT nama, stok,
CASE WHEN stok = 0 THEN 'habis' END AS label_habis
FROM produk
ORDER BY id;
-- CASE bisa mengembalikan angka, bukan hanya teks
SELECT nama, harga,
CASE
WHEN stok = 0 THEN 0
WHEN stok <= 5 THEN 10
ELSE 5
END AS diskon_persen
FROM produk
ORDER BY id;
case-sederhana.sql
Unduh berkas-- bentuk sederhana (simple CASE): satu ekspresi dibandingkan ke daftar nilai
SELECT nama, kategori,
CASE kategori
WHEN 'minuman' THEN 'rak dingin'
WHEN 'bahan pokok' THEN 'rak utama'
ELSE 'rak umum'
END AS lokasi_rak
FROM produk
ORDER BY id;
-- NULLIF adalah CASE yang dibekukan menjadi fungsi
SELECT NULLIF('sama', 'sama') AS nullif_langsung,
CASE WHEN 'sama' = 'sama' THEN NULL ELSE 'sama' END AS case_padanan;
SELECT NULLIF('beda', 'sama') AS nullif_beda,
CASE WHEN 'beda' = 'sama' THEN NULL ELSE 'beda' END AS case_beda;