Menyaring terhadap satu nilai memakai =, terhadap dua nilai bisa memakai OR, tetapi bagaimana dengan lima nama kota atau sepuluh kode produk? Rangkaian OR yang panjang menjadikan query sulit dibaca dan rawan salah ketik. Operator IN menyelesaikannya dengan satu pola ringkas: sebutkan seluruh nilai yang diizinkan dalam satu daftar. Pada chapter ini kita akan berlatih IN pada teks dan angka, membandingkan keterbacaannya melawan OR berantai dengan hasil yang identik, memakai NOT IN untuk mengecualikan daftar, lalu membuktikan satu gotcha terkenal: satu NULL di dalam daftar NOT IN membuat seluruh hasil kosong.
Menyiapkan Tabel Latihan #
Kita pakai kembali tabel produk dan pelanggan dengan data yang sama seperti chapter-chapter sebelumnya:
DROP DATABASE IF EXISTS belajar_filter;
CREATE DATABASE belajar_filter;
USE belajar_filter;Output yang Dihasilkan:
Query OK, 0 rows affected, 1 warning (0.00 sec)
Query OK, 1 row affected (0.00 sec)
Database changedCREATE TABLE produk (
id INT AUTO_INCREMENT PRIMARY KEY,
nama VARCHAR(100) NOT NULL,
kategori VARCHAR(50) NOT NULL,
harga DECIMAL(10,2) NOT NULL,
stok INT NOT NULL DEFAULT 0
);
INSERT INTO produk (nama, kategori, harga, stok) VALUES
('Buku Tulis Sidu 38', 'Alat Tulis', 4500.00, 120),
('Pensil 2B', 'Alat Tulis', 3000.00, 200),
('Penghapus Putih', 'Alat Tulis', 2500.00, 150),
('Pulpen Gel Hitam', 'Alat Tulis', 5000.00, 80),
('Spidol Permanen', 'Alat Tulis', 10000.00, 45),
('Kertas HVS A4', 'Kertas', 27500.00, 20),
('Kertas Origami', 'Kertas', 12000.00, 60),
('Amplop Coklat', 'Kertas', 800.00, 300),
('Stipo 12 Gram', 'Perekat', 6000.00, 0),
('Selotip Bening', 'Perekat', 8000.00, 55);Output yang Dihasilkan:
Query OK, 0 rows affected (0.00 sec)
Query OK, 10 rows affected (0.00 sec)
Records: 10 Duplicates: 0 Warnings: 0CREATE TABLE pelanggan (
id INT AUTO_INCREMENT PRIMARY KEY,
nama VARCHAR(100) NOT NULL,
kota VARCHAR(50) NOT NULL,
telepon VARCHAR(20) NULL
);
INSERT INTO pelanggan (nama, kota, telepon) VALUES
('Budi Santoso', 'Bandung', '0812-5000-1001'),
('Siti Rahayu', 'Jakarta', '0813-5000-1002'),
('Andi Wijaya', 'Surabaya', NULL),
('Dewi Lestari', 'Bandung', '0815-5000-1004'),
('Rian Hidayat', 'Yogyakarta', NULL),
('Maya Putri', 'Medan', '0817-5000-1006'),
('Eko Prasetyo', 'Semarang', NULL),
('Fitri Handayani', 'Bandung', '0819-5000-1008');Output yang Dihasilkan:
Query OK, 0 rows affected (0.00 sec)
Query OK, 8 rows affected (0.00 sec)
Records: 8 Duplicates: 0 Warnings: 0Sintaks IN #
Operator IN diikuti daftar nilai di dalam tanda kurung, dipisahkan koma. Query berikut mengambil seluruh produk Kertas dan Perekat:
SELECT nama, kategori
FROM produk
WHERE kategori IN ('Kertas', 'Perekat')
ORDER BY id;Output yang Dihasilkan:
+----------------+----------+
| nama | kategori |
+----------------+----------+
| Kertas HVS A4 | Kertas |
| Kertas Origami | Kertas |
| Amplop Coklat | Kertas |
| Stipo 12 Gram | Perekat |
| Selotip Bening | Perekat |
+----------------+----------+
5 rows in set (0.00 sec)Lima produk lolos. MySQL menilai kondisi kategori IN ('Kertas', 'Perekat') sebagai benar bila kategori sama dengan salah satu anggota daftar — secara teknis kondisi ini identik dengan kategori = 'Kertas' OR kategori = 'Perekat'. Daftar boleh memuat berapa pun nilai; bertambahnya anggota daftar hanya menambah isi tanda kurung, bukan menambah baris query.
IN untuk Angka dan Teks #
Daftar IN tidak dibatasi tipe teks. Berikut contoh daftar angka untuk menyaring harga-harga tertentu:
SELECT nama, harga
FROM produk
WHERE harga IN (5000, 12000, 27500)
ORDER BY id;Output yang Dihasilkan:
+------------------+----------+
| nama | harga |
+------------------+----------+
| Pulpen Gel Hitam | 5000.00 |
| Kertas HVS A4 | 27500.00 |
| Kertas Origami | 12000.00 |
+------------------+----------+
3 rows in set (0.00 sec)SELECT nama, kota
FROM pelanggan
WHERE kota IN ('Bandung', 'Medan')
ORDER BY id;Output yang Dihasilkan:
+-----------------+---------+
| nama | kota |
+-----------------+---------+
| Budi Santoso | Bandung |
| Dewi Lestari | Bandung |
| Maya Putri | Medan |
| Fitri Handayani | Bandung |
+-----------------+---------+
4 rows in set (0.00 sec)Query pertama menemukan tiga produk dengan harga yang tercantum di daftar, dan query kedua menemukan empat pelanggan Bandung dan Medan. Perhatikan bahwa nilai angka ditulis tanpa tanda kutip sedangkan nilai teks memakai tanda kutip tunggal — aturan literal yang sama seperti pada chapter WHERE.
IN versus OR Berantai #
Setiap kondisi IN selalu punya padanan OR berantai. Dua query berikut sengaja kita jalankan berdampingan:
SELECT nama, kategori FROM produk WHERE kategori IN ('Kertas', 'Perekat') ORDER BY id;
SELECT nama, kategori FROM produk WHERE kategori = 'Kertas' OR kategori = 'Perekat' ORDER BY id;Output yang Dihasilkan:
+----------------+----------+
| nama | kategori |
+----------------+----------+
| Kertas HVS A4 | Kertas |
| Kertas Origami | Kertas |
| Amplop Coklat | Kertas |
| Stipo 12 Gram | Perekat |
| Selotip Bening | Perekat |
+----------------+----------+
5 rows in set (0.00 sec)
+----------------+----------+
| nama | kategori |
+----------------+----------+
| Kertas HVS A4 | Kertas |
| Kertas Origami | Kertas |
| Amplop Coklat | Kertas |
| Stipo 12 Gram | Perekat |
| Selotip Bening | Perekat |
+----------------+----------+
5 rows in set (0.00 sec)Kedua hasilnya identik: lima baris yang sama. Perbedaannya murni keterbacaan. Bayangkan daftar yang tumbuh menjadi delapan kategori — versi IN tetap satu baris dengan delapan nilai, sedangkan versi OR menjadi kalimat panjang dengan delapan pengulangan nama kolom. Setiap pengulangan adalah kesempatan salah ketik: satu kategori = 'Kertass' lolos kompilasi dan diam-diam menggugurkan satu syarat. Dengan IN, nama kolom cukup ditulis sekali.
NOT IN untuk Mengecualikan Daftar #
Menambahkan NOT di depan IN membalik maknanya: baris lolos bila nilainya tidak ada di daftar. Query berikut mengambil pelanggan di luar Bandung dan Semarang:
SELECT nama, kota
FROM pelanggan
WHERE kota NOT IN ('Bandung', 'Semarang')
ORDER BY id;Output yang Dihasilkan:
+--------------+------------+
| nama | kota |
+--------------+------------+
| Siti Rahayu | Jakarta |
| Andi Wijaya | Surabaya |
| Rian Hidayat | Yogyakarta |
| Maya Putri | Medan |
+--------------+------------+
4 rows in set (0.00 sec)Empat pelanggan dari Jakarta, Surabaya, Yogyakarta, dan Medan lolos. Pola NOT IN sangat lazim untuk mengecualikan daftar hitam kecil: akun internal yang tidak boleh tampil di laporan, kategori yang sedang tidak dijual, atau kode gudang yang dihapus.
Jebakan NULL di Dalam Daftar NOT IN #
Kita lanjutkan contoh sebelumnya dengan satu perubahan mungil yang merusak total: daftar kini memuat NULL:
SELECT nama, kota
FROM pelanggan
WHERE kota NOT IN ('Bandung', NULL)
ORDER BY id;Output yang Dihasilkan:
Empty set (0.00 sec)Hasilnya kosong — padahal empat pelanggan tadi seharusnya tetap lolos. Untuk memahami penyebabnya, kita evaluasi satu nilai di luar daftar langsung:
SELECT 'Medan' NOT IN ('Bandung', NULL) AS hasil_cek;Output yang Dihasilkan:
+-----------+
| hasil_cek |
+-----------+
| NULL |
+-----------+
1 row in set (0.00 sec)Hasilnya NULL, bukan 1. MySQL membaca NOT IN sebagai rangkaian perbandingan: 'Medan' <> 'Bandung' AND 'Medan' <> NULL. Perbandingan pertama benar, tetapi perbandingan kedua terhadap NULL menghasilkan NULL — dan 1 AND NULL menghasilkan NULL. Karena klausa WHERE hanya meloloskan baris yang bernilai benar, seluruh baris gugur tanpa pesan error.
Dalam praktik, NULL biasanya masuk daftar bukan karena diketik manual, melainkan karena daftar tersebut datang dari subquery atas kolom yang memuat NULL — pola yang akan kita temui nanti di Section Subqueries. Dua pertahanan utamanya: pastikan daftar bebas NULL, atau saring nilai NULL lebih dulu dengan IS NOT NULL.
Membersihkan Database Latihan #
Latihan daftar nilai selesai. Kita jatuhkan database latihan bersama kedua tabelnya:
DROP DATABASE belajar_filter;Output yang Dihasilkan:
Query OK, 2 rows affected (0.00 sec)Referensi #
- Comparison Functions and Operators — MySQL 8.4 Reference Manual
- Logical Operators — MySQL 8.4 Reference Manual
- SELECT Statement — MySQL 8.4 Reference Manual
Operator IN menuntaskan penyaringan terhadap daftar nilai diskret. Pertanyaan berikutnya berbentuk rentang: “harga antara lima ribu sampai sepuluh ribu”, “pesanan sepanjang Februari”. Kondisi rentang bisa ditulis dengan >= AND <=, tetapi MySQL menyediakan operator BETWEEN yang jauh lebih ringkas dibaca — bahan chapter berikutnya.