Query SQL Lambat? Ini 8 Penyebab Paling Sering
Blog/Tutorial SQL/Query SQL Lambat? Ini 8 Penyebab Paling Sering

Query SQL Lambat? Ini 8 Penyebab Paling Sering

BimaBima
·31 Maret 2026·10 menit baca

Penulis

Bima

Bima

Founder & Data Professional

Bagikan

TL;DR

Query SQL lambat biasanya gara-gara database harus baca seluruh isi tabel (full table scan) padahal cuma butuh sebagian kecil baris. Penyebab paling sering: kolom filter gak punya index, kolom di WHERE dibungkus fungsi sehingga index-nya mati, SELECT * yang narik semua kolom, dan JOIN tanpa kunci yang bener. Jalanin EXPLAIN dulu sebelum nebak-nebak.

Query SQL lambat hampir selalu punya sebab yang sama: database kepaksa baca seluruh isi tabel padahal kamu cuma butuh 200 baris.

Bukan gara-gara servernya lemot. Bukan gara-gara datanya kegedean. Data 50 juta baris tetap bisa dijawab dalam 2 detik kalau query-nya bener.

Ini 8 penyebab yang paling sering aku temuin, diurut dari yang paling sering — plus cara benerinnya.

Gimana cara tau penyebab query lambat?

Jangan nebak. Jalanin EXPLAIN dulu.

EXPLAIN nunjukin rencana eksekusi — langkah apa aja yang bakal ditempuh database buat jawab query kamu. Tabel mana yang dibaca, index mana yang dipakai, berapa baris yang diproses.

EXPLAIN ANALYZE
SELECT kota, SUM(total_harga) AS omzet
FROM transaksi
WHERE tanggal >= '2026-03-01'
GROUP BY kota;

Yang kamu cari di hasilnya: kata Seq Scan (di PostgreSQL) atau full table scan (di MySQL) di tabel yang gede.

Itu artinya database baca semua baris satu per satu. Kalau tabelnya 30 juta baris, ya lama.

Lawannya: Index Scan. Itu artinya database langsung loncat ke baris yang relevan.

Angka lain yang penting: rows (perkiraan baris yang diproses) dan actual time. Kalau perkiraan bilang 100 tapi aktualnya 2 juta, statistik tabelnya basi — jalanin ANALYZE.

1. Kolom yang difilter gak punya index

Penyebab nomor satu. Gak ada saingan.

Index adalah struktur bantu yang bikin database bisa nemu baris tanpa baca semuanya. Mirip daftar isi di buku.

Tanpa index di kolom tanggal, query ini baca semua 30 juta baris:

SELECT * FROM transaksi
WHERE tanggal >= '2026-03-01';

Benerinnya:

CREATE INDEX idx_transaksi_tanggal ON transaksi(tanggal);

Aturan praktis: kolom yang sering muncul di WHERE, JOIN, atau ORDER BY layak dikasih index.

Tapi jangan asal index semua kolom. Tiap index bikin operasi INSERT dan UPDATE jadi lebih lambat, soalnya index-nya harus ikut diperbarui.

2. Index-nya ada, tapi dimatiin sama fungsi

Ini jebakan yang bikin banyak orang frustasi. Index udah dibikin, tapi query tetap lambat.

Penyebabnya: kolom filter dibungkus fungsi.

-- Index di kolom tanggal jadi gak kepakai
SELECT * FROM transaksi
WHERE YEAR(tanggal) = 2026;

Begitu tanggal dibungkus YEAR(), database gak bisa lagi pakai index-nya. Dia harus hitung YEAR() buat tiap baris dulu, baru bandingin.

Tulis ulang jadi rentang:

SELECT id_transaksi, kota, total_harga
FROM transaksi
WHERE tanggal >= '2026-01-01'
  AND tanggal < '2027-01-01';

Sekarang index-nya kepakai. Hasilnya sama persis, tapi kecepatannya beda jauh.

Pola yang sama berlaku buat UPPER(kota) = 'BANDUNG', SUBSTRING(kode, 1, 3) = 'JKT', dan semua fungsi lain yang nempel di kolom.

3. SELECT * padahal cuma butuh 3 kolom

SELECT * maksa database baca dan kirim semua kolom — termasuk kolom teks panjang yang gak kamu lihat.

Efek yang lebih parah: dia matiin covering index.

Covering index itu index yang udah nyimpen semua kolom yang kamu minta. Database bisa jawab langsung dari index, gak perlu balik ke tabel aslinya.

Begitu kamu minta 20 kolom padahal index-nya nyimpen 3, database kepaksa balik ke tabel buat tiap baris. Lambat.

Sebut kolomnya. Ribet dikit di awal, hemat banyak di akhir.

4. JOIN tanpa kunci yang bener

JOIN antara tabel 100.000 baris dan tabel 50.000 baris, tanpa kondisi JOIN yang bener, ngasilin 5 miliar baris perantara.

Database bakal ngitungnya. Terus buang hampir semuanya. Ini yang namanya cartesian product.

-- Bahaya: kondisi JOIN-nya kelewat
SELECT t.id_transaksi, p.nama_produk
FROM transaksi t, produk p
WHERE t.total_harga > 100000;

Selalu tulis JOIN eksplisit dengan ON:

SELECT t.id_transaksi, p.nama_produk
FROM transaksi t
JOIN produk p ON t.id_produk = p.id_produk
WHERE t.total_harga > 100000;

Dan pastiin kolom kunci di kedua sisi punya index. Kolom id_produk di tabel transaksi sering kelupaan diindex, padahal dia yang dipakai buat nyocokin.

Kalau kamu masih ragu bedanya, baca dulu cara kerja JOIN.

5. Filter ditaruh di HAVING, padahal harusnya di WHERE

Urutan eksekusi SQL: FROM → WHERE → GROUP BY → HAVING → SELECT.

WHERE nyaring baris sebelum agregasi. HAVING nyaring setelah agregasi.

-- Buruk: ngitung semua kota dulu, baru buang 11 dari 12
SELECT kota, SUM(total_harga) AS omzet
FROM transaksi
GROUP BY kota
HAVING kota = 'Bandung';

Query di atas ngitung SUM buat 12 kota, terus buang 11 hasilnya. Boros.

-- Bagus: saring dulu, baru agregasi
SELECT kota, SUM(total_harga) AS omzet
FROM transaksi
WHERE kota = 'Bandung'
GROUP BY kota;

Aturannya: pakai HAVING cuma kalau filternya nyentuh hasil agregasi — kayak HAVING SUM(total_harga) > 10000000. Selain itu, taruh di WHERE.

6. Subquery yang jalan berulang tiap baris

Ini namanya correlated subquery. Subquery-nya nyebut kolom dari query luar, jadi dia kepaksa jalan ulang buat tiap baris.

-- Subquery jalan 30 juta kali
SELECT t.id_transaksi, t.total_harga,
       (SELECT nama_kota FROM cabang c WHERE c.id_cabang = t.id_cabang) AS kota
FROM transaksi t;

Ganti pakai JOIN. Database bisa proses sekali jalan:

SELECT t.id_transaksi, t.total_harga, c.nama_kota AS kota
FROM transaksi t
JOIN cabang c ON c.id_cabang = t.id_cabang;

Optimizer modern kadang bisa nerjemahin subquery jadi JOIN sendiri. Tapi jangan diandelin — tulis JOIN dari awal.

7. Wildcard di depan LIKE

-- Index mati
SELECT * FROM produk WHERE nama_produk LIKE '%indomie%';

Wildcard % di depan bikin index gak kepakai. Index itu diurut dari huruf pertama — kalau huruf pertamanya bebas, gak ada yang bisa dilompati.

-- Index kepakai
SELECT * FROM produk WHERE nama_produk LIKE 'indomie%';

Kalau kamu beneran butuh cari di tengah teks, pakai full-text search. Bukan LIKE.

8. ORDER BY di kolom yang gak diindex

Ngurutin 5 juta baris tanpa index = database bikin file sortir sementara di disk. Lambat banget.

Kalau kamu sering nulis ORDER BY tanggal DESC LIMIT 100, kasih index ke kolom tanggal. Database bisa langsung ambil 100 baris teratas dari index tanpa nyortir apa-apa.

Contoh kasus: query 4 menit jadi 3 detik

Ini kejadian nyata di dataset toko_berkah — tabel transaksi 4,2 juta baris dari 12 cabang.

Query awalnya begini, dipakai buat laporan bulanan:

SELECT *
FROM transaksi t
WHERE YEAR(t.tanggal) = 2026
  AND MONTH(t.tanggal) = 3
  AND UPPER(t.kota) = 'BANDUNG';

Waktu jalan: 4 menit 12 detik.

Hasil EXPLAIN: Seq Scan di tabel transaksi. 4,2 juta baris dibaca semua. Index di kolom tanggal ada, tapi mati gara-gara dibungkus YEAR() dan MONTH().

Perbaikannya cuma 2 hal:

  1. Ganti fungsi tanggal jadi rentang.
  2. Sebut kolomnya, jangan SELECT *.
SELECT t.id_transaksi, t.tanggal, t.kota, t.total_harga
FROM transaksi t
WHERE t.tanggal >= '2026-03-01'
  AND t.tanggal < '2026-04-01'
  AND t.kota = 'Bandung';

Waktu jalan: 3,1 detik. Dari 252 detik jadi 3 detik — 80 kali lebih cepat.

Baris yang dibaca database turun dari 4.200.000 jadi 31.480.

Gak ada server baru. Gak ada tuning database. Cuma nulis ulang WHERE-nya.

Urutan diagnosis yang aku pakai

LangkahYang dicekTanda masalah
1Jalanin EXPLAIN ANALYZEAda Seq Scan di tabel gede
2Lihat klausa WHEREAda fungsi nempel di kolom
3Cek index kolom filterKolom filter gak punya index
4Cek kondisi JOINKolom kunci gak diindex
5Cek daftar kolom SELECTMasih pakai SELECT *
6Cek HAVINGAda filter yang gak nyentuh agregasi

Enam langkah ini nutup sekitar 90% kasus query lambat yang pernah aku pegang.

FAQ

Gimana cara tau penyebab query SQL lambat?

Jalanin EXPLAIN atau EXPLAIN ANALYZE di depan query kamu. Perintah itu nunjukin rencana eksekusi database: tabel mana yang dibaca, index mana yang kepakai, dan berapa baris yang diperkirakan diproses. Yang kamu cari kata Seq Scan atau full table scan di tabel gede. Itu tanda database baca semua baris padahal cuma butuh sebagian.

Apakah SELECT * bikin query lambat?

Iya, terutama di tabel yang punya banyak kolom atau kolom teks panjang. SELECT * maksa database baca dan kirim semua kolom, termasuk yang gak kamu pakai. Efek lain yang lebih parah: dia matiin covering index. Kalau index kamu cuma nyimpen 3 kolom dan kamu minta 20 kolom, database tetap harus balik ke tabel aslinya.

Kenapa query jadi lambat padahal udah bikin index?

Biasanya index-nya ada tapi gak kepakai. Penyebab paling sering: kolom filter dibungkus fungsi, kayak WHERE YEAR(tanggal) = 2026 atau WHERE UPPER(kota) = 'BANDUNG'. Begitu kolom dibungkus fungsi, database gak bisa pakai index-nya. Tulis ulang filternya jadi rentang: WHERE tanggal >= '2026-01-01' AND tanggal < '2027-01-01'.

Apa bedanya filter di WHERE dan di HAVING?

WHERE nyaring baris sebelum agregasi, HAVING nyaring hasil setelah agregasi. Kalau kamu naruh filter yang gak butuh hasil agregasi di HAVING, database bakal ngitung semua grup dulu baru buang sebagian besar. Buang-buang kerja. Taruh filter di WHERE selama filter itu gak nyentuh hasil SUM, COUNT, atau AVG.

Query lambat gara-gara datanya kegedean, apa masih bisa dipercepat?

Hampir selalu bisa. Data 50 juta baris tetap bisa dijawab dalam hitungan detik kalau index-nya bener dan filternya nyaring lebih dulu. Yang bikin lambat bukan ukuran tabel, tapi berapa baris yang kepaksa dibaca database. Kalau tetap berat setelah index dibenerin, pertimbangkan tabel ringkasan yang di-refresh tiap malam.

Penutup

Dua hal yang paling sering nolong:

  • Jangan bungkus kolom filter pakai fungsi. Itu matiin index, dan ini penyebab nomor satu yang orang gak sadar.
  • Baca EXPLAIN sebelum nebak. Tiga menit baca rencana eksekusi ngirit tiga jam trial-and-error.

Ambil query paling lambat di kerjaan kamu sekarang, tempelin EXPLAIN ANALYZE di depannya, dan lihat ada Seq Scan atau nggak.

Lanjut ke panduan JOIN lengkap kalau query kamu banyak gabung tabel, atau pahami dulu apa itu index database dan cara kerja GROUP BY.

Buat detail cara baca hasil EXPLAIN, dokumentasi PostgreSQL soal EXPLAIN ini yang paling jelas penjelasannya.

Coba Langsung

Mau praktek langsung? Mulai latihan SQL gratis

Latihan interaktif, langsung di browser.

Buka NgulikSQL →
Bagikan:
Bima
Ditulis oleh

Bima

Founder & Data Professional

Founder Ngulik Data. Passionate about making data analysis accessible for everyone.

Artikel Terkait

Churn Rate SQL: Deteksi Pelanggan yang Kabur Sejak Dini
Tutorial SQL
20 Mei 2026•11 menit baca

Churn Rate SQL: Deteksi Pelanggan yang Kabur Sejak Dini

Studi kasus toko_berkah: 1.240 pelanggan hilang dalam setahun, dan sinyalnya udah kelihatan 2 bulan sebelumnya. Ini query yang bikin kamu nangkap mereka lebih awal.

BimaBima
YoY Growth SQL: Bandingkan Performa dengan Tahun Lalu
Tutorial SQL
17 Mei 2026•11 menit baca

YoY Growth SQL: Bandingkan Performa dengan Tahun Lalu

Hitung YoY growth di SQL pakai LAG atau self-join, lengkap dengan cara ngindarin pembagi nol dan jebakan bulan yang bolong. Contohnya pakai data toko_berkah.

BimaBima
MoM Growth SQL: Hitung Pertumbuhan Bulanan Otomatis
Tutorial SQL
14 Mei 2026•10 menit baca

MoM Growth SQL: Hitung Pertumbuhan Bulanan Otomatis

Berhenti copy-paste omzet bulan lalu ke Excel buat ngitung growth. Satu query SQL pakai LAG bisa ngasih pertumbuhan bulanan otomatis, lengkap sampai bulan yang datanya kosong.

BimaBima
Kembali ke Blog
Ngulik Data logoNgulik Data

Platform edukasi data lengkap untuk professionals Indonesia. Belajar SQL, Data Analysis, dan lebih banyak lagi dengan praktek langsung dan feedback real-time.

Copyright © 2026 - All rights reserved

LINKS
SupportPricingDatasetBlogAffiliates
LEGAL
Terms of servicesPrivacy policy
Ngulik Data
DatasetLeaderboardBlogStore