TL;DR
Operator IN di SQL dipakai buat nyocokin sebuah kolom dengan daftar nilai, misalnya WHERE kota IN ('Bandung', 'Medan'), dan hasilnya sama dengan rangkaian OR tapi jauh lebih enak dibaca. Operator BETWEEN nyaring nilai di sebuah rentang, misalnya WHERE total BETWEEN 45000 AND 90000, dan batas atas maupun bawahnya ikut terhitung. Hati-hati dengan NOT IN: kalau daftarnya mengandung NULL, hasilnya selalu kosong.
IN dan BETWEEN itu dua operator yang bikin klausa WHERE kamu jadi enak dibaca.
WHERE kota IN ('Bandung', 'Medan')
WHERE total BETWEEN 45000 AND 90000
Sekilas nggak ada yang aneh. Tapi dua baris itu punya perilaku yang sering bikin angka laporan meleset. Nggak ada error yang ngasih tau.
IN adalah operator yang ngecek apakah nilai sebuah kolom ada di dalam daftar yang kamu kasih. Kalau ada, barisnya lolos filter. Hasilnya sama persis kayak nulis beberapa kondisi OR, tapi jauh lebih pendek.
-- Versi panjang
WHERE kota = 'Bandung' OR kota = 'Medan' OR kota = 'Jakarta'
-- Versi IN
WHERE kota IN ('Bandung', 'Medan', 'Jakarta')
Tiga kondisi jadi satu baris. Di query yang punya 8 kota, bedanya kerasa banget.
IN dipakai di klausa WHERE, sama kayak operator perbandingan lain.
BETWEEN nyaring nilai yang ada di sebuah rentang, dan batas atas maupun bawahnya ikut kehitung. Ini yang paling sering disalahpahami.
WHERE total BETWEEN 45000 AND 90000
-- Sama persis dengan:
WHERE total >= 45000 AND total <= 90000
Perhatiin tanda sama dengannya. BETWEEN itu inklusif di dua sisi.
Tabel penjualan toko_berkah, 10 transaksi Januari 2026.
| id | kota | kategori | total |
|---|---|---|---|
| 1 | Bandung | Minuman | 30.000 |
| 2 | Bandung | Makanan | 45.000 |
| 3 | Surabaya | Minuman | 45.000 |
| 4 | Surabaya | Sembako | 120.000 |
| 5 | Medan | Makanan | 90.000 |
| 6 | Bandung | Sembako | 120.000 |
| 7 | Medan | Minuman | 60.000 |
| 8 | Surabaya | Makanan | 45.000 |
| 9 | Bandung | Minuman | 30.000 |
| 10 | Medan | Sembako | 120.000 |
Bos minta daftar transaksi "menengah", antara 45.000 sampai 90.000.
SELECT id_transaksi, total FROM penjualan
WHERE total BETWEEN 45000 AND 90000;
-- id 2 (45000), id 3 (45000), id 5 (90000), id 7 (60000), id 8 (45000)
-- 5 baris
Sekarang coba yang eksklusif.
SELECT id_transaksi, total FROM penjualan
WHERE total > 45000 AND total < 90000;
-- id 7 (60000)
-- 1 baris
BETWEEN 45000 AND 90000 di toko_berkah ngasih 5 baris. Versi eksklusifnya cuma 1. Selisih 4 baris cuma gara-gara satu keputusan soal batas, dan nggak ada satu pun pesan error yang ngasih tau kamu salah pilih.
Sebelum nulis BETWEEN, tanya dulu: batasnya ikut atau nggak? Kalau bos bilang "di atas 45 ribu", itu eksklusif. Kalau bilang "mulai dari 45 ribu", itu inklusif.
Ini jebakan yang paling mahal, dan hampir semua analis pernah kena.
-- BERBAHAYA kalau kolomnya timestamp
WHERE tanggal BETWEEN '2026-01-01' AND '2026-01-31'
Database ngartiin '2026-01-31' sebagai 2026-01-31 00:00:00, tengah malam. Transaksi yang terjadi jam 09:15 tanggal 31 Januari nilainya lebih besar dari tengah malam, jadi nggak ikut kesaring.
Kamu kehilangan satu hari penuh transaksi. Di toko yang ramai, itu jutaan rupiah yang hilang dari laporan.
Pola yang aman:
WHERE tanggal >= '2026-01-01'
AND tanggal < '2026-02-01'
Batas bawah inklusif, batas atas eksklusif ke awal bulan berikutnya. Pola ini bener buat kolom DATE maupun timestamp, dan nggak perlu kamu ubah kalau tipe kolomnya berubah nanti.
Aku pakai pola ini di semua laporan bulanan. Nggak pernah pakai BETWEEN buat tanggal lagi.
Ini bug favorit SQL. Anggap ada tabel kota_ditutup yang isinya cabang yang lagi tutup.
CREATE TABLE kota_ditutup (kota TEXT);
INSERT INTO kota_ditutup VALUES ('Surabaya'), (NULL);
Baris NULL itu masuk gara-gara ada input kosong. Sekarang cari kota yang nggak tutup.
SELECT kota FROM penjualan
WHERE kota NOT IN (SELECT kota FROM kota_ditutup);
-- 0 baris 😵
Nol baris. Padahal Bandung dan Medan jelas nggak ada di daftar itu.
Penyebabnya: NOT IN itu sama dengan <> 'Surabaya' AND <> NULL. Dan SQL nggak bisa mastiin sebuah nilai "berbeda dari NULL", soalnya NULL artinya "nggak tau". Hasil perbandingannya jadi nggak tau juga, dan barisnya gagal lolos.
Satu baris NULL bikin seluruh hasil query hilang. Tanpa error. Detail perilakunya di glosarium NULL.
Dua cara benerinnya:
-- A: buang NULL dari subquery
WHERE kota NOT IN (
SELECT kota FROM kota_ditutup WHERE kota IS NOT NULL
)
-- B: pakai NOT EXISTS (lebih aman, lebih cepat)
WHERE NOT EXISTS (
SELECT 1 FROM kota_ditutup k WHERE k.kota = penjualan.kota
)
Aku selalu pilih B. NOT EXISTS kebal dari jebakan NULL dan biasanya lebih ngebut di tabel besar.
Buat daftar pendek, IN dan OR sama cepatnya. Database ngoptimasi keduanya jadi rencana yang mirip.
Yang bikin lambat itu daftar yang kepanjangan. Kalau kamu nempel 5.000 ID pelanggan ke dalam IN (...), query-nya bakal berat dan bahkan bisa nabrak batas ukuran query.
Kalau daftarnya panjang, masukin ke tabel sementara terus JOIN. Jauh lebih cepat.
Aturan lengkap soal operator perbandingan bisa kamu cek di dokumentasi PostgreSQL untuk comparison functions.
>= dan <.BETWEEN 90000 AND 45000 selalu ngasih 0 baris. Angka kecil duluan.Iya, inklusif dua-duanya. BETWEEN 45000 AND 90000 sama dengan >= 45000 AND <= 90000. Baris yang nilainya tepat di batas ikut kesaring.
Daftarnya kemungkinan mengandung NULL. SQL nggak bisa mastiin sebuah nilai berbeda dari NULL, jadi semua baris gagal lolos. Pakai NOT EXISTS.
Buat daftar pendek, sama aja. Keunggulan IN ada di keterbacaan. Kalau daftarnya ratusan nilai, pakai tabel sementara plus JOIN.
Bisa: WHERE kota IN (SELECT kota FROM cabang_aktif). Tapi kalau subquery-nya bisa ngasih NULL, lebih aman pakai EXISTS.
>= dan <, jangan BETWEEN.Semua query di artikel ini udah dites dan siap copy-paste. Tinggal kamu tempel ke editor SQL kamu, nggak perlu install apa-apa.
Lanjut baca: Operator LIKE SQL buat nyari teks pakai wildcard.
Mau praktek langsung? Mulai latihan SQL gratis
Latihan interaktif, langsung di browser.
Analisa per kuartal, hari kerja, atau musim jadi ribet kalau tiap query ngitung ulang atribut tanggal. Tabel kalender nyimpen semua atribut itu sekali, biar tinggal di-JOIN.
Laporan penjualan harian sering bolong di hari tanpa transaksi. Ini cara bikin deret tanggal lengkap di SQL biar tiap hari muncul, walau nilainya nol.
Struktur organisasi tersimpan sebagai kolom id_atasan yang saling nunjuk. Ini cara narik rantai jabatan, hitung total bawahan, dan span of control cuma pakai SQL.