EXISTS vs IN di SQL: Perbedaan dan Performa (2026)
TL;DR
EXISTS dan IN sama-sama dipakai buat nyaring baris berdasarkan hasil subquery, tapi cara kerjanya beda. IN ngumpulin semua nilai subquery lalu ngecek kecocokan, sedangkan EXISTS berhenti begitu nemu satu baris yang cocok. EXISTS biasanya lebih cepat buat subquery besar dan aman dari jebakan NULL, sedangkan IN lebih ringkas buat daftar nilai yang kecil dan tetap.
EXISTS dan IN sama-sama dipakai buat nyaring baris berdasarkan hasil dari subquery. Bedanya ada di cara mereka ngecek kecocokan, dan itu ngaruh ke performa dan hasil pas ada nilai NULL.
Salah pilih di antara keduanya bisa bikin query kamu lambat di tabel besar, atau lebih parah, ngasih hasil yang salah tanpa error apa pun. Dua-duanya kelihatan mirip, jadi banyak orang pakai asal jadi.
Di sini aku jelasin beda cara kerjanya, mana yang lebih cepat, dan jebakan NULL yang sering bikin bug diam-diam.
Apa beda EXISTS dan IN di SQL?
IN ngumpulin semua nilai dari subquery jadi satu daftar, lalu ngecek apakah nilai baris ada di daftar itu. EXISTS jalanin subquery buat tiap baris dan berhenti begitu nemu satu kecocokan, balikin true atau false. IN peduli sama nilainya, EXISTS cuma peduli apakah ada baris yang cocok.
Contoh IN, nyari pelanggan yang pernah transaksi:
SELECT nama
FROM pelanggan
WHERE id IN (SELECT pelanggan_id FROM transaksi);Contoh EXISTS buat pertanyaan yang sama:
SELECT nama
FROM pelanggan p
WHERE EXISTS (
SELECT 1 FROM transaksi t
WHERE t.pelanggan_id = p.id
);Perhatiin EXISTS pakai SELECT 1. Angka ini nggak penting, karena EXISTS cuma ngecek ada atau nggaknya baris, bukan isinya. Subquery-nya juga nyambung ke tabel luar lewat t.pelanggan_id = p.id, ini disebut correlated subquery.
EXISTS atau IN, mana yang lebih cepat?
Jawabannya tergantung ukuran subquery dan database yang kamu pakai. Tapi ada pola umum yang bisa jadi pegangan. EXISTS biasanya lebih cepat kalau subquery-nya balikin banyak baris, karena dia berhenti di kecocokan pertama tanpa nunggu seluruh daftar kekumpul.
IN sering lebih efisien kalau subquery-nya balikin sedikit nilai yang tetap, atau kalau kamu nulis daftar manual kayak WHERE kota IN ('Jakarta', 'Bandung'). Di kasus ini nggak ada subquery yang mahal buat dijalanin.
| Situasi | Pilihan lebih baik |
|---|---|
| Subquery balikin ribuan baris | EXISTS |
| Daftar nilai kecil dan tetap | IN |
| Subquery bisa berisi NULL | EXISTS |
| Cek keberadaan baris terkait | EXISTS |
| Filter kolom sama satu daftar literal | IN |
Catatan penting: database modern kayak PostgreSQL punya query optimizer yang sering nulis ulang IN jadi bentuk yang mirip EXISTS di belakang layar. Jadi buat kasus sederhana, selisih performanya bisa kecil. Bedanya baru kerasa di tabel besar dengan jutaan baris.
Kenapa IN bisa ngasih hasil salah waktu ada NULL?
Ini jebakan paling bahaya dari IN. Kalau subquery IN balikin nilai NULL di antara hasilnya, kondisi NOT IN bisa nggak ngembaliin baris apa pun, padahal kamu ngarep ada hasil. Ini terjadi tanpa error, jadi gampang kelewat.
Contoh yang bermasalah, nyari pelanggan yang belum pernah transaksi:
-- BAHAYA kalau pelanggan_id ada yang NULL
SELECT nama
FROM pelanggan
WHERE id NOT IN (SELECT pelanggan_id FROM transaksi);Kalau kolom pelanggan_id ada satu aja yang NULL, query ini balikin nol baris. Penyebabnya cara SQL ngebandingin nilai sama NULL: hasilnya bukan true atau false, tapi "unknown", dan NOT IN nolak semua baris yang statusnya unknown.
NOT EXISTS aman dari masalah ini:
-- AMAN dari jebakan NULL
SELECT nama
FROM pelanggan p
WHERE NOT EXISTS (
SELECT 1 FROM transaksi t
WHERE t.pelanggan_id = p.id
);Aturan praktis yang aku pegang: buat NOT IN, selalu pastikan subquery-nya nggak mungkin balikin NULL, atau ganti aja pakai NOT EXISTS. Buat ngerti konsep terkait, cek juga semi join dan anti join di SQL.
Contoh Kasus: Pelanggan Aktif toko_berkah
Anggap toko_berkah punya tabel pelanggan berisi 8.000 baris dan tabel transaksi berisi 120.000 baris. Kamu mau tau pelanggan mana yang pernah belanja minimal sekali.
-- Versi EXISTS
SELECT p.nama, p.kota
FROM pelanggan p
WHERE EXISTS (
SELECT 1 FROM transaksi t
WHERE t.pelanggan_id = p.id
);Di dataset uji seukuran ini, versi EXISTS umumnya lebih ngebut karena begitu satu transaksi ketemu buat seorang pelanggan, subquery langsung berhenti dan lanjut ke pelanggan berikutnya. Dia nggak perlu ngitung semua 120.000 transaksi.
Kalau kamu pakai IN buat pertanyaan yang sama, database harus ngumpulin daftar semua pelanggan_id dari 120.000 transaksi dulu. Buat daftar sebesar ini, ngumpulin dan ngeceknya makan lebih banyak kerja. Selisihnya kecil di data mini, tapi kerasa di tabel ratusan ribu baris ke atas.
Buat cek kebalikannya, pelanggan yang belum pernah belanja, pakai NOT EXISTS biar aman dari NULL seperti dijelasin tadi.
Kapan sebaiknya pakai IN?
IN tetap pilihan yang bagus dan lebih gampang dibaca di beberapa situasi:
- Daftar nilai literal.
WHERE kota IN ('Jakarta', 'Bandung', 'Surabaya')jauh lebih ringkas daripada nulis EXISTS. - Subquery kecil. Kalau subquery cuma balikin belasan nilai, IN gampang dibaca dan performanya nggak masalah.
- Nggak ada risiko NULL. Buat kondisi IN biasa (bukan NOT IN), NULL nggak sebahaya di NOT IN, jadi lebih aman dipakai.
Buat pemula, saran aku: pakai IN buat daftar pendek yang jelas, pakai EXISTS buat cek keberadaan di tabel besar dan buat semua kasus NOT. Aturan ini nutup mayoritas kebutuhan sehari-hari tanpa harus mikir panjang.
Kesalahan Umum
- Pakai NOT IN di kolom yang bisa NULL. Ini penyebab bug diam paling sering. Ganti pakai NOT EXISTS atau tambahin
WHERE kolom IS NOT NULLdi subquery. - Nulis SELECT * di dalam EXISTS. Nggak salah, tapi nggak perlu. Pakai
SELECT 1biar niatnya jelas: cuma cek ada atau nggak. - Lupa correlated condition di EXISTS. Kalau subquery EXISTS nggak nyambung ke tabel luar, dia bakal selalu true selama ada baris. Pastikan ada kondisi yang ngehubungin kayak
t.pelanggan_id = p.id.
FAQ
EXISTS selalu lebih cepat dari IN?
Nggak selalu. EXISTS unggul kalau subquery balikin banyak baris, karena berhenti di kecocokan pertama. Tapi buat daftar nilai kecil atau literal, IN sama cepat atau bahkan lebih ringkas. Database modern juga sering nulis ulang IN jadi bentuk mirip EXISTS otomatis, jadi selisihnya kecil di kasus sederhana. Yang lebih nentuin performa biasanya index dan ukuran data, bukan cuma pilihan EXISTS atau IN.
Apa beda NOT IN dan NOT EXISTS?
Keduanya nyari baris yang nggak punya kecocokan, tapi beda perilaku pas ada NULL. NOT IN bisa balikin nol baris kalau subquery-nya berisi satu saja nilai NULL, karena SQL ngitung perbandingan sama NULL sebagai unknown. NOT EXISTS nggak kena masalah ini dan tetap ngasih hasil bener. Buat kasus NOT, NOT EXISTS umumnya pilihan yang lebih aman kecuali kamu yakin subquery bebas NULL.
Apa itu correlated subquery di EXISTS?
Correlated subquery adalah subquery yang nyambung ke tabel luar lewat kolom bersama, misalnya t.pelanggan_id = p.id. Subquery ini dijalanin sekali buat tiap baris tabel luar, dan hasilnya tergantung baris yang lagi diproses. EXISTS hampir selalu pakai bentuk ini. Bedanya sama subquery biasa yang jalan sekali dan hasilnya sama buat semua baris.
Kenapa EXISTS pakai SELECT 1, bukan SELECT nama kolom?
Karena EXISTS cuma ngecek ada atau nggaknya baris, bukan isinya. Angka 1 di SELECT 1 nggak dipakai buat apa pun, dia cuma placeholder. Kamu bisa nulis SELECT apa saja di situ dan hasilnya sama. Konvensi pakai SELECT 1 bikin niat query jelas: kita cuma peduli keberadaan baris, bukan nilai kolomnya. Ini juga ngasih sinyal ke pembaca kode soal maksud kamu.
Bisa ganti EXISTS pakai JOIN?
Sering bisa, dan kadang JOIN lebih cepat. INNER JOIN bisa gantiin EXISTS buat nyari baris yang punya kecocokan. Tapi hati-hati, JOIN bisa gandain baris kalau tabel kedua punya banyak kecocokan per baris, sedangkan EXISTS selalu ngasih satu baris per baris asal. Kalau kamu cuma butuh cek keberadaan tanpa ngambil kolom dari tabel kedua, EXISTS lebih aman dari duplikat.
Penutup
Ringkasnya: pakai EXISTS buat cek keberadaan di tabel besar dan semua kasus NOT, pakai IN buat daftar nilai kecil yang jelas. EXISTS berhenti di kecocokan pertama, IN ngumpulin semua nilai dulu.
Yang paling perlu kamu inget: NOT IN plus NULL sama dengan bug diam. Kalau ragu subquery-mu bisa berisi NULL, pilih NOT EXISTS.
Mau dalami konsep terkait? Lanjut ke semi join dan anti join di SQL. Detail teknis resmi ada di dokumentasi PostgreSQL.
Mau praktek langsung? Mulai latihan SQL gratis
Latihan interaktif, langsung di browser.
Artikel terkait
Semi Join dan Anti Join di SQL: Filter Pakai Tabel Lain
Semi join ngambil baris yang punya pasangan di tabel lain, anti join ngambil yang nggak punya. Keduanya filter, bukan gabung kolom. Ini cara nulisnya di SQL pakai EXISTS dan NOT EXISTS.
Crosstab (Pivot) di PostgreSQL: Ubah Baris Jadi Kolom
Crosstab di PostgreSQL mivot data dari format panjang (baris) jadi format lebar (kolom), mirip pivot table Excel. Butuh extension tablefunc, dan ada alternatif pakai CASE atau FILTER kalau kolomnya tetap.
INSERT ON CONFLICT di PostgreSQL
INSERT ON CONFLICT bikin PostgreSQL insert baris baru atau update yang udah ada dalam satu perintah, tanpa error gara-gara duplikat primary key.