Menu

Subquery SQLite: SELECT Bersarang di WHERE, FROM, dan SELECT

Cara menaruh satu SELECT di dalam SELECT lain di SQLite: scalar subquery, IN/EXISTS, derived table, correlated subquery, dan kapan JOIN lebih mudah dibaca.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

Subquery Adalah SELECT di Dalam SELECT

Subquery persis seperti namanya: pernyataan SELECT yang diselipkan di dalam pernyataan lain dan dibungkus tanda kurung. SQLite menjalankan query di dalam, mengambil hasilnya, lalu memberikannya ke query di luar.

Siapkan contoh kecil yang bisa kita pakai berulang kali:

Lima pesanan, empat pelanggan, dua di antaranya belum memesan apa pun. Kita akan memakai data ini sepanjang halaman.

Subquery di WHERE: Memfilter dengan Daftar

Bentuk yang paling umum: ambil daftar id di query dalam, lalu filter query luar berdasarkan daftar itu.

Query dalam menghasilkan setiap customer_id yang muncul di orders. Query luar hanya menyimpan pelanggan yang id-nya ada di daftar itu. Cleo, Boris, dan Ada muncul; Dmitri (tanpa pesanan) tidak.

IN (SELECT ...) adalah pola andalan untuk "baris di A yang punya pasangan di B." Bacalah dalam hati sebagai "di mana nilai kolom ini adalah salah satu nilai yang dikembalikan query dalam."

NOT IN: Waspadai NULL

Pertanyaan kebalikannya, "pelanggan mana yang belum memesan?", cukup berbeda satu baris:

Di sini berhasil. Tapi NOT IN punya sisi tajam: jika subquery pernah mengembalikan NULL, seluruh NOT IN menjadi NULL (yang bukan TRUE), dan kamu mendapat nol baris. Mengejutkan, dan tanpa peringatan.

Kebiasaan yang aman saat memakai NOT IN terhadap kolom yang mungkin berisi NULL:

Atau pakai NOT EXISTS, yang sama sekali tidak punya masalah ini. Kita akan membahasnya nanti.

Scalar Subquery: Satu Baris, Satu Kolom

Scalar subquery mengembalikan satu nilai, yaitu satu baris dan satu kolom, dan kamu bisa memakainya di mana pun sebuah nilai diharapkan.

Query dalam SELECT MAX(total) FROM orders mengembalikan 200. Query luar lalu memfilter pesanan yang cocok dengan nilai itu. Berguna setiap kali kamu perlu membandingkan dengan hasil agregat.

Kamu juga bisa memakai scalar subquery di daftar SELECT untuk menempelkan nilai hasil perhitungan ke setiap baris:

Setiap baris customers menjalankan query dalam satu kali, dengan customers.id dimasukkan ke dalamnya. Itulah correlated subquery, yang dibahas lebih lanjut di bawah. Untuk kasus "satu angka per baris" seperti ini, LEFT JOIN dengan GROUP BY biasanya lebih cepat, tapi bentuk scalar ini enak sekali dibaca.

EXISTS: Cukup Periksa Apakah Ada yang Cocok

EXISTS adalah sepupu IN yang lebih kalem. Ia tidak peduli pada nilai, hanya memeriksa apakah subquery mengembalikan baris apa pun. Biasanya kamu menulis SELECT 1 di dalamnya, karena kolomnya tidak penting.

Query ini mencari pelanggan yang pernah membuat setidaknya satu pesanan di atas 100. Query dalam merujuk ke c.id dari query luar, dan itulah yang membuatnya correlated. SQLite berhenti memindai tabel dalam begitu menemukan kecocokan, itulah sebabnya EXISTS sering lebih cepat daripada IN untuk pertanyaan "apakah baris ini punya baris terkait?".

Negasinya, NOT EXISTS, adalah cara yang aman terhadap NULL untuk menanyakan "tidak ada baris terkait":

Subquery di FROM: Derived Table

Subquery bisa menggantikan tabel di mana saja, termasuk di klausa FROM. Query dalam menjadi "derived table" sementara yang diberi nama, yang bisa kamu join, filter, atau agregasi.

Query dalam menghitung total per pelanggan. Query luar merata-ratakan total itu per negara. Agregasi dua tahap seperti ini memang kegunaan derived table, saat kamu tidak bisa mengerjakan semuanya dalam satu GROUP BY.

Alias AS per_customer wajib ada: setiap derived table butuh nama.

Correlated Subquery: Dijalankan per Baris Luar

Subquery disebut correlated saat merujuk ke kolom dari query luar. SQLite harus mengevaluasi ulang query dalam untuk setiap baris luar, yang fleksibel tapi bisa jadi mahal.

Untuk setiap pelanggan, cari pesanan terbesarnya. Query dalam bergantung pada customers.id, jadi ia dijalankan sekali per pelanggan. Pelanggan tanpa pesanan mendapat NULL, dan memang itulah yang kamu inginkan.

Correlated subquery paling cocok untuk "untuk setiap baris di A, hitung sesuatu dari B." Jika tabelnya kecil atau pencariannya memakai index, tidak masalah. Pada tabel besar tanpa index pendukung, ukur dulu performanya sebelum dirilis: JOIN dengan GROUP BY sering lebih cepat.

Subquery vs JOIN: Pilih yang Mana?

Dua query ini menjawab pertanyaan yang sama:

Keduanya mengembalikan baris yang sama. Optimizer SQLite sering menulis ulang satu bentuk menjadi bentuk lainnya secara internal. Pilih berdasarkan kemudahan dibaca:

  • Pakai subquery saat kamu hanya perlu memfilter, dan tidak ingin kolom dari tabel dalam mengotori hasilnya.
  • Pakai JOIN saat hasilnya butuh kolom dari kedua tabel.
  • Pakai EXISTS saat kamu bertanya "apakah ada setidaknya satu baris terkait?". Bentuk ini lebih jelas dan menghindari jebakan NULL pada IN/NOT IN.

Kalau ragu, tulis versi yang menjelaskan dirinya sendiri saat dibaca keras-keras.

Jebakan Umum: Subquery yang Mengembalikan Banyak Baris

Subquery yang dipakai dengan = harus mengembalikan paling banyak satu baris. Jika mengembalikan lebih, SQLite memilih salah satunya (praktis secara acak) dan kamu diam-diam mendapat hasil yang salah, tanpa error.

Pakai IN saat query dalam bisa mengembalikan banyak baris:

Jika kamu mengharapkan tepat satu baris dan ingin memastikannya, tambahkan LIMIT 1 dan ORDER BY supaya pilihannya setidaknya deterministik. Lebih baik lagi: tulis query-nya sehingga satu baris dijamin oleh datanya (filter pada kolom yang unik).

Berikutnya: Common Table Expression

Subquery di FROM cepat jadi sulit dikelola, terutama saat kamu butuh derived table yang sama dua kali, atau saat sarangnya sampai tiga tingkat. Common Table Expression (WITH ... AS (...)) memungkinkanmu memberi nama sebuah subquery di awal lalu merujuknya dengan nama itu di sisa pernyataan. Itu halaman berikutnya.

Pertanyaan yang sering diajukan

Apa itu subquery di SQLite?

Subquery adalah pernyataan SELECT yang bersarang di dalam pernyataan lain dan dibungkus tanda kurung. SQLite menjalankan query di dalam lalu memberikan hasilnya ke query di luar. Subquery bisa muncul di WHERE, FROM, SELECT, dan beberapa klausa lainnya.

Apa perbedaan IN dan EXISTS di SQLite?

IN (SELECT ...) memeriksa apakah sebuah nilai cocok dengan salah satu baris yang dikembalikan subquery. EXISTS (SELECT ...) hanya memeriksa apakah subquery menghasilkan baris apa pun, tanpa peduli nilainya. EXISTS biasanya pilihan yang lebih baik saat query di dalam merujuk ke baris luar (correlated subquery).

Sebaiknya pakai subquery atau JOIN di SQLite?

Pakai JOIN saat kamu butuh kolom dari kedua tabel di hasilnya. Pakai subquery saat kamu hanya perlu memfilter atau menghitung satu nilai. Optimizer SQLite sering menulis ulang satu bentuk menjadi bentuk lainnya, jadi pilih saja yang lebih jelas dibaca.

Apa itu correlated subquery di SQLite?

Correlated subquery merujuk ke kolom dari query luar, sehingga harus dievaluasi ulang untuk setiap baris luar. Bentuk ini fleksibel tapi bisa lambat pada tabel besar. Jika correlated subquery menjadi titik lambat, menulis ulang sebagai JOIN atau CTE sering membantu.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai