50 Latihan Soal SQL Pemula + Kunci Jawaban
TL;DR
Latihan soal SQL pemula paling efektif kalau pakai satu dataset yang sama dari awal sampai akhir, biar kamu hafal strukturnya dan bisa fokus ke logikanya. Halaman ini berisi 50 soal bertingkat, dari SELECT dasar sampai window function, semuanya pakai dataset toko_berkah berisi transaksi UMKM retail Indonesia. Kunci jawaban disediakan lengkap, tapi pola belajar yang paling berhasil adalah nyoba 10 menit dulu sebelum ngintip.
Cara tercepat jago SQL bukan nonton tutorial. Nulis query sendiri, salah, benerin, ulang.
Di bawah ini 50 soal bertingkat yang aku susun dari yang paling gampang sampai yang bikin mikir. Semua pakai satu dataset yang sama, biar kamu nggak buang waktu ngapalin struktur tabel baru tiap soal.
Kunci jawabannya ada semua. Tapi janji dulu: coba 10 menit sendiri sebelum ngintip.
Dataset yang dipakai: toko_berkah
Dataset toko_berkah berisi 18.400 transaksi dari 6 cabang UMKM retail sepanjang 2025. Ada dua tabel.
Tabel transaksi:
| Kolom | Tipe | Keterangan |
|---|---|---|
| id_transaksi | INT | Primary key |
| tanggal | DATE | Tanggal transaksi |
| cabang | TEXT | Jakarta, Bekasi, Depok, Bandung, Surabaya, Semarang |
| id_pelanggan | INT | Foreign key ke tabel pelanggan |
| kategori | TEXT | Sembako, Snack, Minuman, Perawatan |
| jumlah_item | INT | Banyak barang dibeli |
| total_harga | INT | Nilai transaksi dalam rupiah |
Tabel pelanggan:
| Kolom | Tipe | Keterangan |
|---|---|---|
| id_pelanggan | INT | Primary key |
| nama | TEXT | Nama pelanggan |
| kota | TEXT | Kota domisili |
| tanggal_daftar | DATE | Kapan pertama daftar member |
Level 1: SELECT dan WHERE (soal 1-10)
- Tampilkan semua kolom dari tabel transaksi.
- Tampilkan cuma kolom tanggal, cabang, dan total_harga.
- Tampilkan semua transaksi dari cabang Bekasi.
- Tampilkan transaksi dengan total_harga di atas Rp500.000.
- Tampilkan transaksi kategori Sembako di cabang Depok.
- Tampilkan transaksi antara 1 Oktober dan 31 Desember 2025.
- Tampilkan transaksi yang jumlah_item-nya 5 atau lebih.
- Tampilkan transaksi dari cabang Jakarta ATAU Surabaya.
- Tampilkan transaksi yang total_harga-nya antara Rp100.000 dan Rp300.000.
- Tampilkan daftar kategori unik yang ada di tabel.
Kunci jawaban level 1
-- 1
SELECT * FROM transaksi;
-- 2
SELECT tanggal, cabang, total_harga FROM transaksi;
-- 3
SELECT * FROM transaksi WHERE cabang = 'Bekasi';
-- 4
SELECT * FROM transaksi WHERE total_harga > 500000;
-- 5
SELECT * FROM transaksi
WHERE kategori = 'Sembako' AND cabang = 'Depok';
-- 6
SELECT * FROM transaksi
WHERE tanggal BETWEEN '2025-10-01' AND '2025-12-31';
-- 7
SELECT * FROM transaksi WHERE jumlah_item >= 5;
-- 8
SELECT * FROM transaksi
WHERE cabang IN ('Jakarta', 'Surabaya');
-- 9
SELECT * FROM transaksi
WHERE total_harga BETWEEN 100000 AND 300000;
-- 10
SELECT DISTINCT kategori FROM transaksi;
Soal 8 bisa juga pakai OR. Tapi IN lebih rapi kalau daftarnya panjang.
Level 2: ORDER BY, LIMIT, dan fungsi teks (soal 11-20)
- Tampilkan 10 transaksi dengan nilai terbesar.
- Tampilkan 5 transaksi terlama (tanggal paling awal).
- Urutkan transaksi berdasarkan cabang, lalu total_harga dari besar ke kecil.
- Tampilkan 3 transaksi terbesar di cabang Bandung.
- Tampilkan nama pelanggan dalam huruf kapital semua.
- Tampilkan pelanggan yang namanya diawali huruf A.
- Tampilkan pelanggan yang namanya mengandung kata "Sari".
- Tampilkan panjang karakter nama tiap pelanggan.
- Tampilkan transaksi dengan harga per item, hasil pembagian total_harga dibagi jumlah_item.
- Tampilkan 10 transaksi dengan harga per item terendah.
Kunci jawaban level 2
-- 11
SELECT * FROM transaksi ORDER BY total_harga DESC LIMIT 10;
-- 12
SELECT * FROM transaksi ORDER BY tanggal ASC LIMIT 5;
-- 13
SELECT * FROM transaksi ORDER BY cabang ASC, total_harga DESC;
-- 14
SELECT * FROM transaksi
WHERE cabang = 'Bandung'
ORDER BY total_harga DESC LIMIT 3;
-- 15
SELECT UPPER(nama) AS nama_kapital FROM pelanggan;
-- 16
SELECT * FROM pelanggan WHERE nama LIKE 'A%';
-- 17
SELECT * FROM pelanggan WHERE nama LIKE '%Sari%';
-- 18
SELECT nama, LENGTH(nama) AS panjang_nama FROM pelanggan;
-- 19
SELECT
id_transaksi,
total_harga,
jumlah_item,
ROUND(total_harga::numeric / NULLIF(jumlah_item, 0), 0)
AS harga_per_item
FROM transaksi;
-- 20
SELECT
id_transaksi,
ROUND(total_harga::numeric / NULLIF(jumlah_item, 0), 0)
AS harga_per_item
FROM transaksi
ORDER BY harga_per_item ASC
LIMIT 10;
NULLIF(jumlah_item, 0) di soal 19 buat ngindarin error pembagian dengan nol. Kebiasaan kecil yang nyelametin banyak query.
Level 3: GROUP BY dan agregasi (soal 21-32)
- Hitung total transaksi di seluruh tabel.
- Hitung total omzet seluruh cabang.
- Hitung rata-rata nilai transaksi.
- Hitung omzet per cabang.
- Hitung jumlah transaksi per kategori.
- Hitung rata-rata nilai transaksi per cabang, urutkan dari besar ke kecil.
- Cari nilai transaksi tertinggi dan terendah per cabang.
- Hitung omzet per bulan.
- Hitung omzet per cabang per kategori.
- Tampilkan cabang yang omzetnya di atas Rp500 juta.
- Tampilkan kategori yang jumlah transaksinya lebih dari 3.000.
- Hitung berapa pelanggan unik yang belanja di tiap cabang.
Kunci jawaban level 3
-- 21
SELECT COUNT(*) AS jumlah_transaksi FROM transaksi;
-- 22
SELECT SUM(total_harga) AS total_omzet FROM transaksi;
-- 23
SELECT ROUND(AVG(total_harga), 0) AS rata_rata FROM transaksi;
-- 24
SELECT cabang, SUM(total_harga) AS omzet
FROM transaksi
GROUP BY cabang;
-- 25
SELECT kategori, COUNT(*) AS jumlah
FROM transaksi
GROUP BY kategori;
-- 26
SELECT cabang, ROUND(AVG(total_harga), 0) AS rata_rata
FROM transaksi
GROUP BY cabang
ORDER BY rata_rata DESC;
-- 27
SELECT
cabang,
MAX(total_harga) AS tertinggi,
MIN(total_harga) AS terendah
FROM transaksi
GROUP BY cabang;
-- 28
SELECT
EXTRACT(MONTH FROM tanggal) AS bulan,
SUM(total_harga) AS omzet
FROM transaksi
GROUP BY 1
ORDER BY 1;
-- 29
SELECT cabang, kategori, SUM(total_harga) AS omzet
FROM transaksi
GROUP BY cabang, kategori
ORDER BY cabang, omzet DESC;
-- 30
SELECT cabang, SUM(total_harga) AS omzet
FROM transaksi
GROUP BY cabang
HAVING SUM(total_harga) > 500000000
ORDER BY omzet DESC;
-- 31
SELECT kategori, COUNT(*) AS jumlah
FROM transaksi
GROUP BY kategori
HAVING COUNT(*) > 3000;
-- 32
SELECT cabang, COUNT(DISTINCT id_pelanggan) AS pelanggan_unik
FROM transaksi
GROUP BY cabang;
Soal 30 dan 31 nguji hal yang sama: WHERE nyaring sebelum dikelompokin, HAVING nyaring sesudah. Ini yang paling sering ketuker.
Level 4: JOIN (soal 33-42)
- Tampilkan transaksi lengkap dengan nama pelanggannya.
- Tampilkan transaksi dengan nama dan kota pelanggan.
- Tampilkan semua pelanggan beserta transaksinya, termasuk yang belum pernah belanja.
- Cari pelanggan yang belum pernah transaksi sama sekali.
- Hitung total belanja per pelanggan, tampilkan namanya.
- Cari 10 pelanggan dengan total belanja terbesar.
- Hitung omzet per kota pelanggan.
- Cari pelanggan yang belanja di lebih dari satu cabang.
- Hitung omzet dari pelanggan yang daftar di 2025 aja.
- Bandingkan omzet pelanggan lama (daftar sebelum 2025) vs pelanggan baru.
Kunci jawaban level 4
-- 33
SELECT t.*, p.nama
FROM transaksi t
INNER JOIN pelanggan p ON t.id_pelanggan = p.id_pelanggan;
-- 34
SELECT t.id_transaksi, t.tanggal, t.total_harga, p.nama, p.kota
FROM transaksi t
INNER JOIN pelanggan p ON t.id_pelanggan = p.id_pelanggan;
-- 35
SELECT p.nama, t.id_transaksi, t.total_harga
FROM pelanggan p
LEFT JOIN transaksi t ON p.id_pelanggan = t.id_pelanggan;
-- 36
SELECT p.id_pelanggan, p.nama
FROM pelanggan p
LEFT JOIN transaksi t ON p.id_pelanggan = t.id_pelanggan
WHERE t.id_transaksi IS NULL;
-- 37
SELECT p.nama, SUM(t.total_harga) AS total_belanja
FROM pelanggan p
INNER JOIN transaksi t ON p.id_pelanggan = t.id_pelanggan
GROUP BY p.nama;
-- 38
SELECT p.nama, SUM(t.total_harga) AS total_belanja
FROM pelanggan p
INNER JOIN transaksi t ON p.id_pelanggan = t.id_pelanggan
GROUP BY p.nama
ORDER BY total_belanja DESC
LIMIT 10;
-- 39
SELECT p.kota, SUM(t.total_harga) AS omzet
FROM pelanggan p
INNER JOIN transaksi t ON p.id_pelanggan = t.id_pelanggan
GROUP BY p.kota
ORDER BY omzet DESC;
-- 40
SELECT p.nama, COUNT(DISTINCT t.cabang) AS jumlah_cabang
FROM pelanggan p
INNER JOIN transaksi t ON p.id_pelanggan = t.id_pelanggan
GROUP BY p.nama
HAVING COUNT(DISTINCT t.cabang) > 1;
-- 41
SELECT SUM(t.total_harga) AS omzet_pelanggan_baru
FROM pelanggan p
INNER JOIN transaksi t ON p.id_pelanggan = t.id_pelanggan
WHERE p.tanggal_daftar >= '2025-01-01';
-- 42
SELECT
CASE
WHEN p.tanggal_daftar < '2025-01-01' THEN 'Lama'
ELSE 'Baru'
END AS tipe_pelanggan,
COUNT(DISTINCT p.id_pelanggan) AS jumlah_orang,
SUM(t.total_harga) AS omzet
FROM pelanggan p
INNER JOIN transaksi t ON p.id_pelanggan = t.id_pelanggan
GROUP BY 1;
Soal 36 itu pola klasik: LEFT JOIN plus IS NULL buat nyari yang nggak punya pasangan. Sering keluar di interview.
Level 5: Subquery, CTE, dan window function (soal 43-50)
- Cari transaksi yang nilainya di atas rata-rata seluruh transaksi.
- Cari transaksi yang nilainya di atas rata-rata cabangnya sendiri.
- Pakai CTE buat hitung omzet per cabang, lalu tampilkan yang di atas rata-rata omzet semua cabang.
- Beri ranking transaksi terbesar di tiap cabang.
- Tampilkan 3 transaksi terbesar per cabang.
- Hitung omzet kumulatif per bulan (running total).
- Bandingkan omzet tiap bulan dengan bulan sebelumnya.
- Hitung persentase kontribusi tiap cabang terhadap total omzet.
Kunci jawaban level 5
-- 43
SELECT * FROM transaksi
WHERE total_harga > (SELECT AVG(total_harga) FROM transaksi);
-- 44
SELECT t.*
FROM transaksi t
WHERE t.total_harga > (
SELECT AVG(total_harga)
FROM transaksi
WHERE cabang = t.cabang
);
-- 45
WITH omzet_cabang AS (
SELECT cabang, SUM(total_harga) AS omzet
FROM transaksi
GROUP BY cabang
)
SELECT *
FROM omzet_cabang
WHERE omzet > (SELECT AVG(omzet) FROM omzet_cabang);
-- 46
SELECT
cabang,
id_transaksi,
total_harga,
RANK() OVER (
PARTITION BY cabang
ORDER BY total_harga DESC
) AS peringkat
FROM transaksi;
-- 47
WITH ranked AS (
SELECT
cabang,
id_transaksi,
total_harga,
ROW_NUMBER() OVER (
PARTITION BY cabang
ORDER BY total_harga DESC
) AS rn
FROM transaksi
)
SELECT cabang, id_transaksi, total_harga
FROM ranked
WHERE rn <= 3
ORDER BY cabang, total_harga DESC;
-- 48
WITH omzet_bulanan AS (
SELECT
EXTRACT(MONTH FROM tanggal) AS bulan,
SUM(total_harga) AS omzet
FROM transaksi
GROUP BY 1
)
SELECT
bulan,
omzet,
SUM(omzet) OVER (ORDER BY bulan) AS omzet_kumulatif
FROM omzet_bulanan
ORDER BY bulan;
-- 49
WITH omzet_bulanan AS (
SELECT
EXTRACT(MONTH FROM tanggal) AS bulan,
SUM(total_harga) AS omzet
FROM transaksi
GROUP BY 1
)
SELECT
bulan,
omzet,
LAG(omzet) OVER (ORDER BY bulan) AS omzet_bulan_lalu,
omzet - LAG(omzet) OVER (ORDER BY bulan) AS selisih
FROM omzet_bulanan
ORDER BY bulan;
-- 50
SELECT
cabang,
SUM(total_harga) AS omzet,
ROUND(
100.0 * SUM(total_harga)
/ SUM(SUM(total_harga)) OVER (),
1
) AS persen_kontribusi
FROM transaksi
GROUP BY cabang
ORDER BY omzet DESC;
Soal 50 kelihatan aneh dengan SUM(SUM(...)) OVER (). Yang di dalam ngitung per cabang, yang di luar ngitung total semuanya. Ini trik yang kepake terus di kerjaan.
Contoh kasus: apa yang ketemu dari soal 50
Waktu aku jalanin soal 50 di dataset toko_berkah, hasilnya bikin berhenti sebentar.
| Cabang | Omzet | Kontribusi |
|---|---|---|
| Jakarta | Rp1,42 miliar | 28,4% |
| Surabaya | Rp1,08 miliar | 21,6% |
| Bandung | Rp894 juta | 17,9% |
| Semarang | Rp731 juta | 14,6% |
| Depok | Rp512 juta | 10,2% |
| Bekasi | Rp366 juta | 7,3% |
Dua cabang teratas nyumbang separuh omzet. Bekasi cuma 7,3%, padahal jumlah transaksinya nggak jauh beda dari Depok.
Artinya masalah Bekasi ada di nilai per transaksi, bukan di jumlah pembeli. Satu query, satu temuan.
Kesalahan umum waktu ngerjain latihan SQL
Langsung ngintip kunci jawaban. Otak kamu belajar dari momen macet, bukan dari baca jawaban benar. Kasih 10 menit buat tiap soal.
Ketuker WHERE dan HAVING. WHERE nyaring baris sebelum dikelompokin. HAVING nyaring hasil kelompok. Kalau kamu nulis WHERE SUM(...) > 100, itu bakal error.
Lupa GROUP BY semua kolom non-agregasi. Kalau kamu SELECT cabang dan kategori tapi cuma GROUP BY cabang, hasilnya error di kebanyakan database.
Ngerjain 50 soal dalam sehari. Nggak bakal nempel. Pecah jadi 2 minggu, 5 soal per hari.
Nggak cek hasilnya masuk akal. Query yang jalan bukan berarti query yang benar. Selalu cek jumlah barisnya wajar.
Kalau kamu masih goyang di dasar, balik dulu ke halaman GROUP BY dan JOIN.
FAQ
Berapa lama waktu yang dibutuhkan buat ngerjain 50 soal ini?
Kalau kamu benar-benar pemula, sekitar 10-15 jam total, dan sebaiknya dipecah jadi 2 minggu. Level 1 sampai 3 biasanya lancar dalam 3-4 jam. Level 4 dan 5 yang makan waktu. Ngerjain 5 soal sampai paham betul lebih berguna dari nyontek 50 jawaban dalam sehari.
Apakah query di soal ini jalan di MySQL dan PostgreSQL?
Mayoritas jalan di dua-duanya. Yang beda cuma beberapa fungsi tanggal dan casting angka. Kalau kamu pakai MySQL lama, ganti EXTRACT(MONTH FROM tanggal) jadi MONTH(tanggal). Referensi lengkapnya ada di dokumentasi resmi PostgreSQL.
Harus install database dulu buat latihan SQL?
Nggak harus. Kamu bisa langsung praktek di editor SQL online yang jalan di browser. Kalau kamu mau setup sendiri, SQLite paling ringan dan cukup buat semua soal di halaman ini.
Urutan belajar SQL yang paling efektif buat pemula gimana?
SELECT dan WHERE dulu sampai lancar, lalu ORDER BY dan LIMIT, baru GROUP BY. Setelah itu JOIN, lalu subquery dan CTE, terakhir window function. Banyak orang lompat ke JOIN terlalu cepat lalu bingung, padahal masalahnya karena WHERE-nya belum kuat.
Apakah latihan soal SQL cukup buat lolos interview data analyst?
Cukup buat lolos tes teknisnya, tapi belum cukup buat lolos keseluruhan. Interviewer juga nanya kenapa kamu pilih pendekatan tertentu dan gimana kamu jelasin hasilnya ke orang non-teknis. Latih dua-duanya.
Penutup
Dua hal yang mau aku tinggalin. Kerjain 5 soal per hari, bukan 50 sekaligus. Dan cek hasilnya masuk akal, jangan cuma seneng karena querynya nggak error.
Mau langsung ngetik querynya tanpa install apa-apa? Buka NgulikSQL, editornya jalan di browser.
Baca juga: prospek kerja data analyst 2026 kalau kamu latihan SQL buat pindah karir.
Mau praktek langsung? Mulai latihan SQL gratis
Latihan interaktif, langsung di browser.
Artikel Terkait
7 SQL Client Gratis Terbaik buat Analis Data (2026)
Nggak perlu bayar buat nulis query. Ini 7 SQL client gratis yang beneran dipakai analis, plus kelebihan dan kekurangannya masing-masing.
SQL Style Guide: Cara Nulis Query yang Enak Dibaca Tim
Sepuluh aturan SQL style guide yang bikin query kamu kebaca sama tim — dan sama kamu sendiri 6 bulan lagi. Lengkap dengan contoh sebelum-sesudah.
Take-Home Test SQL Data Analyst: Contoh Soal + Cara Ngerjainnya
Contoh soal take-home test SQL yang beneran dipakai perusahaan, dikerjain step by step — dari baca soal, nulis query, sampai nyusun insight yang bikin recruiter nengok.