Episode ini membahas subquery di clause WHERE FROM dan SELECT, logika percabangan CASE WHEN, fungsi penanganan NULL COALESCE dan NULLIF, serta operasi himpunan UNION, INTERSECT, dan EXCEPT untuk menggabungkan hasil beberapa query.

Selamat datang di episode 7 series Belajar SQL PostgreSQL! Pada episode 6, kita sudah belajar menggabungkan tabel dengan join. Sekarang kita akan memperluas kosakata SQL dengan tiga kemampuan penting: subquery (query di dalam query), conditional expressions (logika percabangan di SQL), dan set operations (operasi matematika himpunan antar hasil query).
Di episode ini, kita akan membahas subquery di clause WHERE dengan operator IN, EXISTS, ANY, dan ALL, subquery di clause FROM (derived tables) dan SELECT, logika percabangan CASE WHEN, fungsi penanganan NULL COALESCE dan NULLIF, lalu operasi himpunan UNION, INTERSECT, dan EXCEPT.
Subquery adalah query SELECT yang bersarang di dalam query lain. Ia bisa muncul di berbagai clause, dan hasilnya bisa berupa nilai tunggal, daftar nilai, atau sebuah tabel penuh.
Posisi paling umum: subquery sebagai filter. Operator yang sering dipakai:
IN: memeriksa apakah nilai termasuk dalam hasil subquery.EXISTS / NOT EXISTS: memeriksa apakah subquery menghasilkan setidaknya satu baris.ANY: membandingkan dengan "setidaknya satu" nilai hasil subquery.ALL: membandingkan dengan "semua" nilai hasil subquery.SELECT email, full_name
FROM users
WHERE id IN (
SELECT customer_id
FROM orders
WHERE total > 1000000
);EXISTS adalah alternatif yang sering lebih efisien karena berhenti di baris pertama yang cocok:
SELECT email, full_name
FROM users u
WHERE EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = u.id
AND o.total > 1000000
);Perhatikan korelasi: subquery di atas mereferensikan u.id dari query luar. Ini disebut correlated subquery — subquery dievaluasi per baris query luar.
Subquery di FROM menghasilkan tabel sementara yang disebut derived table. Ia harus diberi alias, dan kolomnya bisa dipakai seperti tabel biasa:
SELECT email, total_belanja
FROM (
SELECT
customer_id,
SUM(total) AS total_belanja
FROM orders
GROUP BY customer_id
) AS ringkasan
JOIN users u ON u.id = ringkasan.customer_id
ORDER BY total_belanja DESC;Subquery juga bisa menjadi kolom hasil. Ia harus mengembalikan nilai tunggal (scalar subquery):
SELECT
email,
(SELECT COUNT(*) FROM orders o WHERE o.customer_id = u.id) AS jumlah_order
FROM users u
ORDER BY jumlah_order DESC;CASE WHEN adalah cara SQL melakukan if-then-else. Ia mengembalikan nilai berbeda berdasarkan kondisi:
SELECT
name,
price,
CASE
WHEN price > 1000000 THEN 'mahal'
WHEN price > 100000 THEN 'menengah'
ELSE 'terjangkau'
END AS kategori_harga
FROM products;Bentuk CASE value WHEN juga bisa mengecek kesamaan nilai secara ringkas:
SELECT
order_id,
status,
CASE status
WHEN 'new' THEN 'pesanan baru'
WHEN 'paid' THEN 'sudah dibayar'
ELSE 'status lain'
END AS deskripsi_status
FROM orders;Dua fungsi paling berguna untuk menangani NULL:
COALESCE(nilai, default): mengembalikan nilai pertama yang bukan NULL dari daftar argumen.NULLIF(a, b): mengembalikan NULL jika a = b, selain itu mengembalikan a. Berguna untuk menghindari pembagian dengan nol.SELECT
email,
COALESCE(phone, 'belum diisi') AS telepon,
NULLIF(age, 0) AS usia
FROM users;Warning
Jangan tertukar: COALESCE memberi nilai pengganti untuk NULL, sedangkan IS NULL hanya memeriksa keberadaan NULL. Dan ingat, NULL di SQL bukan 0 dan bukan string kosong — ia berarti "nilai tidak diketahui", sehingga NULL = NULL hasilnya bukan TRUE melainkan NULL!
Set operations menggabungkan hasil dari beberapa query — berbeda dengan join yang menggabungkan kolom. Hasilnya adalah baris-baris yang disusun vertikal. Syaratnya: jumlah dan tipe kolom dari setiap query harus kompatibel.
UNION menggabungkan hasil dua query dan menghilangkan duplikat. UNION ALL menggabungkan tanpa menghilangkan duplikat.
SELECT customer_id FROM orders_2025
UNION
SELECT customer_id FROM orders_2026;
SELECT customer_id FROM orders_2025
UNION ALL
SELECT customer_id FROM orders_2026;Query pertama: daftar customer yang pernah order di 2025 atau 2026, unik. Query kedua: semua baris — customer yang order di kedua tahun muncul dua kali. UNION ALL lebih cepat karena tidak perlu proses deduplikasi, jadi pakai ia jika duplikat memang diizinkan.
INTERSECT: baris yang muncul di kedua query.EXCEPT: baris yang muncul di query pertama tapi tidak di query kedua.SELECT customer_id FROM orders_2026
INTERSECT
SELECT customer_id FROM orders_2025;
SELECT customer_id FROM customers_2026
EXCEPT
SELECT customer_id FROM customers_2025;Query pertama: customer yang order di kedua tahun. Query kedua: customer baru (ada di 2026 tapi tidak di 2025).
Inti yang harus dibawa pulang:
WHERE, FROM, dan SELECT — masing-masing punya aturan hasil (nilai, tabel, atau scalar).EXISTS sering lebih efisien untuk correlated subquery.CASE WHEN adalah if-then-else di SQL; COALESCE dan NULLIF adalah senjata melawan NULL.UNION ALL lebih cepat daripada UNION jika deduplikasi tidak diperlukan.INTERSECT dan EXCEPT adalah alat analisis data antar periode yang ampuh.Di episode 8 selanjutnya, kita akan melihat sisi PostgreSQL yang membuatnya disebut database hybrid: Advanced Data Types: JSONB & Array Support — mulai dari perbedaan JSON dan JSONB, operator query JSON seperti -> dan @>, fungsi manipulasi jsonb_set, hingga tipe data array dengan operator ANY, @>, dan UNNEST. Bersiaplah, karena di sinilah PostgreSQL mulai terasa istimewa.