Subquery — SQL

Subquery adalah query di dalam query lain. Dievaluasi terlebih dahulu, hasilnya digunakan oleh query utama. Scalar subquery (mengembalikan satu nilai): SELECT…

Subquery adalah query di dalam query lain, ditulis di dalam kurung. Yang di dalam jalan duluan, lalu hasilnya dipakai oleh query luar. Mirip pertanyaan "siapa yang tingginya di atas rata-rata kelas?": kamu harus menghitung rata-ratanya dulu (pertanyaan kecil), baru membandingkan setiap siswa dengan angka itu (pertanyaan besar).

🧪 Tebakan cepat: tabel products berisi Laptop 12.000.000, Mouse 150.000, Monitor 3.500.000, Keyboard 500.000 dan Headset 750.000. Berapa produk yang harganya di atas rata-rata? Tebak dulu, jawabannya ada di bagian "Langkah demi langkah".

Kenapa butuh subquery

Cara yang paling terpikir duluan gak jalan:

SELECT name, price FROM products
WHERE price > AVG(price);
-- misuse of aggregate function AVG()

WHERE memeriksa satu baris sekaligus: "harga baris ini lebih dari ...?". Sedangkan AVG(price) butuh semua baris untuk dihitung. Dua hal itu gak bisa dicampur di satu WHERE, makanya SQLite menolak. Solusinya: hitung rata-ratanya di query terpisah, lalu taruh query itu di dalam kurung.

Langkah demi langkah

SELECT name, price
FROM products
WHERE price > (
  SELECT AVG(price) FROM products
);

Langkah 1, query dalam. SELECT AVG(price) FROM products menjumlahkan semua harga, 16.900.000, lalu membaginya dengan 5 produk: 3.380.000. Hasilnya satu angka.

Langkah 2, query luar. Kurung tadi diganti angkanya, jadi query-nya sekarang WHERE price > 3380000, dan setiap baris diperiksa:

name price price > 3380000?
Laptop 12.000.000 ya
Mouse 150.000 tidak
Monitor 3.500.000 ya
Keyboard 500.000 tidak
Headset 750.000 tidak

Hasilnya 2 produk: Laptop dan Monitor. Itu jawaban tebakannya.

Subquery yang menghasilkan daftar: IN

Subquery juga bisa menghasilkan satu kolom berisi banyak baris. Pakai dengan IN, artinya "ada di daftar ini". Pakai tabel pelanggan dan pesanan dari pelajaran JOIN (Budi, Siti, Andi, dan pesanan Laptop, Mouse, Monitor, Flashdisk):

SELECT name FROM customers
WHERE id IN (SELECT customer_id FROM orders);

Query dalam menghasilkan daftar 1, 1, 2, NULL. Query luar menyimpan pelanggan yang id-nya ada di daftar itu: Budi dan Siti, masing-masing sekali, walau Budi punya dua pesanan. Itu kelebihan IN dibanding JOIN kalau kamu cuma butuh "siapa saja", bukan "pesanan apa saja".

📌 Subquery di SELECT

Subquery juga bisa jadi kolom. Yang ini jalan sekali untuk setiap pelanggan, karena dia memakai c.id dari query luar:

SELECT c.name,
  (SELECT COUNT(*) FROM orders o WHERE o.customer_id = c.id) AS jumlah
FROM customers c;

Budi 2, Siti 1, Andi 0. Subquery yang memakai kolom dari query luar seperti ini disebut correlated subquery.

⚠️ Jebakan yang sering ketemu

TL;DR: Subquery = query di dalam kurung yang jalan duluan, lalu hasilnya dipakai query luar. WHERE price > (SELECT AVG(price) FROM products) menghitung rata-rata 3.380.000 dulu, lalu menyimpan Laptop dan Monitor. WHERE price > AVG(price) error karena WHERE memeriksa satu baris, sedangkan AVG butuh semua baris. Subquery satu nilai dipakai dengan >, <, =. Subquery daftar dipakai dengan IN. Hati-hati NOT IN kalau daftarnya bisa berisi NULL: hasilnya jadi kosong.

Yang akan kamu pelajari