Soal 1Ketika hasil EXPLAIN QUERY PLAN berubah dari SCAN perf_sales menjadi SEARCH perf_sales USING INDEX ..., apa artinya?
Indeks — Komposit, Parsial, Ekspresi, dan UNIQUE
Pada tabel perf_sales 50.000 baris, kamu akan membuat indeks dengan CREATE INDEX, membangun indeks komposit, indeks parsial pada status='pending', indeks ekspresi pada amount/100, dan indeks UNIQUE pada sale_id, lalu memperhatikan EXPLAIN QUERY PLAN beralih dari FULL SCAN (SCAN) ke INDEX SCAN (SEARCH).
Dataset untuk artikel ini — tabel perf_sales
Sebuah indeks bekerja seperti indeks di bagian belakang buku: ia membuat kamu bisa menemukan baris dengan nilai kolom tertentu secara cepat.
Artikel ini menelusuri pembuatan indeks dengan CREATE INDEX, membangun indeks komposit, parsial, ekspresi, dan UNIQUE, serta memakai EXPLAIN QUERY PLAN untuk mengonfirmasi bagaimana strategi pencarian berubah.
CREATE INDEX dan EXPLAIN QUERY PLAN — ubah full table scan menjadi index seek
Buat indeks dengan CREATE INDEX nama_indeks ON tabel(kolom);.
Ketika kamu memakai kolom yang diindeks dalam kondisi kesetaraan atau rentang WHERE, optimizer bisa memilih pencarian lewat indeks alih-alih full table scan dan langsung melompat ke baris yang cocok.
Periksa strategi pencarian dengan EXPLAIN QUERY PLAN.
Beri prefix kueri kamu dengan EXPLAIN QUERY PLAN dan kamu akan melihat rencana bagaimana baris akan ditemukan alih-alih hasil sebenarnya.
Tanpa indeks kamu akan melihat SCAN perf_sales (juga disebut FULL SCAN — periksa setiap baris di tabel), dan setelah indeks bekerja kamu akan melihat sesuatu seperti SEARCH perf_sales USING INDEX ... (juga disebut INDEX SCAN — persempit pencarian dengan indeks).
Kata-kata persis dari plan bervariasi tergantung lingkungan, jadi artikel ini memperlakukan eksekusi yang berhasil sebagai jawaban yang benar dan membaca makna plan dalam prosa dan penjelasan.
-- Plan tanpa indeks (contoh read-only terpisah)
EXPLAIN QUERY PLAN
SELECT sale_id, amount FROM perf_sales WHERE region = 'East';
--> SCAN perf_sales (periksa setiap baris)
-- Buat indeks, lalu lihat plan yang sama
DROP INDEX IF EXISTS ix_region;
CREATE INDEX ix_region ON perf_sales(region);
EXPLAIN QUERY PLAN
SELECT sale_id, amount FROM perf_sales WHERE region = 'East';
--> SEARCH perf_sales USING INDEX ix_region (region=?)
-- Catatan: jalankan DROP -> CREATE -> EXPLAIN end-to-end dalam eksekusi yang sama
Indeks komposit — indeks beberapa kolom bersama-sama
Menulis beberapa kolom di dalam tanda kurung — CREATE INDEX nama ON tabel(col1, col2); — menghasilkan indeks komposit (juga disebut indeks multi-kolom).
Ini mempercepat kueri yang memakai kondisi dari kolom pertama dan seterusnya, seperti WHERE col1 = ... AND col2 >= ....
Urutan kolom penting: indeks komposit tidak akan membantu kueri yang tidak memakai kolom pertama dalam klausa WHERE-nya.
Kalau pola akses kamu tetap — misalnya "kesetaraan pada emp_id dan rentang pada sale_date" — membangun indeks komposit dengan urutan itu membuat satu indeks bisa mencakup kedua kondisi.
-- Indeks komposit (contoh read-only terpisah)
DROP INDEX IF EXISTS ix_demo;
CREATE INDEX ix_demo ON perf_sales(region, sale_date);
EXPLAIN QUERY PLAN
SELECT sale_id FROM perf_sales
WHERE region = 'East' AND sale_date >= '2024-01-01';
--> SEARCH perf_sales USING INDEX ix_demo (region=? AND sale_date>?)
-- Persempit berdasarkan region (kesetaraan), lalu persempit sale_date berdasarkan rentang
Indeks parsial dan indeks ekspresi
Indeks parsial dibangun dengan CREATE INDEX nama ON tabel(kolom) WHERE kondisi; dan mengindeks hanya sebagian baris dalam tabel.
Kalau kamu sering mencari baris dengan nilai tertentu seperti status = 'pending', mengindeks hanya baris itu membuat indeks tetap kecil dan fokus pada kueri yang sebenarnya kamu jalankan.
Di perf_sales, hanya 5.000 baris (10% dari total) yang memiliki status = 'pending', jadi indeks parsial dengan WHERE status = 'pending' cocok secara alami.
Indeks ekspresi dibangun dengan CREATE INDEX nama ON tabel(ekspresi); dan mengindeks hasil ekspresi yang dihitung alih-alih kolom mentah.
Pakai ini untuk kueri yang menerapkan ekspresi yang sama di sisi pencarian, seperti WHERE amount / 100 = ....
Indeks biasa pada sebuah kolom umumnya tidak membantu setelah kamu menerapkan fungsi atau aritmetika pada kolom itu, tapi kalau kamu mengindeks ekspresinya sendiri, pencarian yang memakai ekspresi yang cocok bisa memakainya.
-- Indeks parsial (contoh read-only terpisah)
DROP INDEX IF EXISTS ix_part;
CREATE INDEX ix_part ON perf_sales(sale_date) WHERE status = 'refunded';
EXPLAIN QUERY PLAN
SELECT sale_id FROM perf_sales
WHERE status = 'refunded' AND sale_date >= '2024-01-01';
--> SEARCH perf_sales USING ... INDEX ix_part (sale_date>?)
-- Indeks ekspresi
DROP INDEX IF EXISTS ix_expr;
CREATE INDEX ix_expr ON perf_sales(qty * amount);
EXPLAIN QUERY PLAN
SELECT sale_id FROM perf_sales WHERE qty * amount = 100000;
--> SEARCH perf_sales USING ... INDEX ix_expr (<expr>=?)
Indeks UNIQUE — indeks yang melarang duplikat
Indeks UNIQUE — dibangun dengan CREATE UNIQUE INDEX nama ON tabel(kolom); — mempercepat pencarian dan mencegah nilai duplikat dimasukkan ke kolom.
Kalau kamu mencoba membuat indeks UNIQUE di kolom yang sudah berisi duplikat, kamu akan mendapat error UNIQUE constraint failed dan indeksnya tidak akan dibuat sama sekali.
Sisi positifnya: kalau kamu membuatnya di kolom yang nilainya unik (primary key atau business key yang unik), kamu mendapat pencegahan duplikat plus pencarian kesetaraan yang cepat.
Di perf_sales, product (200 nilai berbeda) memiliki banyak pengulangan, jadi indeks UNIQUE tidak mungkin di sana. sale_id, di sisi lain, adalah primary key dan unik, jadi kamu bisa membangunnya.
Latihan berikutnya membuat indeks UNIQUE pada sale_id, mengonfirmasi bahwa kolom unik menerima indeks dan bahwa pencarian kesetaraan pada kolom itu memakainya.
-- Indeks UNIQUE (contoh read-only terpisah)
DROP INDEX IF EXISTS ix_u_demo;
CREATE UNIQUE INDEX ix_u_demo ON perf_sales(sale_id);
EXPLAIN QUERY PLAN
SELECT sale_id FROM perf_sales WHERE sale_id = 12345;
-- Mencoba menambah UNIQUE pada kolom dengan duplikat ditolak
-- CREATE UNIQUE INDEX ix_bad ON perf_sales(product);
--> UNIQUE constraint failed: perf_sales.product
-- product memiliki 200 nilai berbeda yang berulang di 50.000 baris, jadi tidak bisa dijadikan UNIQUE
Tips — kapan menambahkan indeks
Aturan praktis: indeks cenderung mengalahkan SCAN ketika kamu bisa mempersempit hasil ke sekitar 15-20% atau kurang dari tabel. Di atas ambang itu, optimizer sering memilih SCAN secara otomatis.
- Tambahkan: pencarian kesetaraan atau rentang pada kolom tabel besar, primary key / foreign key / kolom UNIQUE, kunci JOIN, kolom yang dirujuk oleh ORDER BY / GROUP BY
- Lewati: tabel kecil dengan beberapa ribu baris atau kurang, kolom selektivitas rendah seperti gender, tabel berat-tulis, LIKE '%foo%' (wildcard di depan tidak bisa memakai indeks)
Alur kerja baseline: periksa plan SCAN / SEARCH dengan EXPLAIN QUERY PLAN dan tambahkan indeks hanya pada kueri yang lambat.
Cek Pemahaman
Jawab setiap pertanyaan satu per satu.
Soal 2Kueri mana yang paling diuntungkan dari indeks komposit CREATE INDEX ix ON perf_sales(emp_id, sale_date);?
Soal 3Apa yang terjadi ketika kamu menjalankan CREATE UNIQUE INDEX pada kolom product, yang berisi duplikat?