Pernah nggak kamu lihat teman yang bisa bilang, "Gue udah buat database sendiri buat project!" — dan kamu cuma manggut-manggut padahal dalam hati penasaran setengah mati? Nah, itulah momen yang kamu tunggu. Di artikel penutup seri ini, kita akan melakukan studi kasus MySQL project secara langsung: merancang database toko online dari nol, mengisi data, lalu menjalankan berbagai query analitik yang beneran berguna di dunia nyata.
Ini adalah artikel ke-28 — alias artikel terakhir dari seri "Belajar Query SQL MySQL". Selama 27 artikel sebelumnya, kamu sudah belajar mulai dari SELECT sederhana, JOIN, subquery, agregasi, hingga transaksi. Sekarang saatnya semua ilmu itu dipraktikkan dalam satu studi kasus MySQL project yang nyata dan terstruktur. Anggap saja ini "ujian praktek"-mu sebelum wisuda sebagai MySQL Developer pemula! 🎓
Di artikel ini kamu akan merancang skema database toko online dengan 5 tabel, mengisi data sampel, lalu menjawab pertanyaan bisnis nyata lewat query SQL. Siap? Let's go!
Tokopedia dilaporkan memproses lebih dari 10 juta transaksi per hari pada puncak harbolnas. Di balik itu semua, ada sistem database yang bekerja keras memastikan setiap pesanan, stok, dan pembayaran tercatat dengan akurat. Bahkan platform e-commerce skala kecil pun membutuhkan desain database yang solid — dan itu dimulai dari pemahaman yang sama seperti yang sedang kamu pelajari sekarang!
1. Merancang Skema Database Toko Online
Sebelum nulis satu baris SQL pun, seorang developer handal selalu mulai dengan merancang skema — menentukan tabel apa saja yang dibutuhkan dan bagaimana hubungan antar tabelnya. Ibarat arsitek yang gambar denah dulu sebelum bangun rumah.
Untuk toko online kita, kita butuh 5 tabel inti dengan relasi yang saling terhubung:
| Tabel | Fungsi | Kolom Utama | Relasi ke |
|---|---|---|---|
| kategori | Kelompok produk | id_kategori, nama_kategori | — |
| produk | Data barang jual | id_produk, nama, harga, stok, id_kategori | kategori |
| pelanggan | Data pembeli | id_pelanggan, nama, email, kota | — |
| pesanan | Header transaksi | id_pesanan, id_pelanggan, tgl_pesan, status | pelanggan |
| detail_pesanan | Item per pesanan | id_detail, id_pesanan, id_produk, qty, harga_saat_beli | pesanan, produk |
CREATE DATABASE db_toko_online; USE db_toko_online; CREATE TABLE kategori ( id_kategori INT PRIMARY KEY AUTO_INCREMENT, nama_kategori VARCHAR(50) NOT NULL ) ENGINE=InnoDB; CREATE TABLE produk ( id_produk INT PRIMARY KEY AUTO_INCREMENT, nama_produk VARCHAR(100) NOT NULL, harga DECIMAL(12,2) NOT NULL, stok INT DEFAULT 0, id_kategori INT, FOREIGN KEY (id_kategori) REFERENCES kategori(id_kategori) ) ENGINE=InnoDB; CREATE TABLE pelanggan ( id_pelanggan INT PRIMARY KEY AUTO_INCREMENT, nama VARCHAR(80) NOT NULL, email VARCHAR(100) UNIQUE, kota VARCHAR(50) ) ENGINE=InnoDB; CREATE TABLE pesanan ( id_pesanan INT PRIMARY KEY AUTO_INCREMENT, id_pelanggan INT, tgl_pesan DATE NOT NULL, status ENUM('diproses','dikirim','selesai','batal') DEFAULT 'diproses', FOREIGN KEY (id_pelanggan) REFERENCES pelanggan(id_pelanggan) ) ENGINE=InnoDB; CREATE TABLE detail_pesanan ( id_detail INT PRIMARY KEY AUTO_INCREMENT, id_pesanan INT, id_produk INT, qty INT NOT NULL, harga_saat_beli DECIMAL(12,2) NOT NULL, FOREIGN KEY (id_pesanan) REFERENCES pesanan(id_pesanan), FOREIGN KEY (id_produk) REFERENCES produk(id_produk) ) ENGINE=InnoDB;
Perhatikan kolom harga_saat_beli di tabel detail_pesanan. Kenapa tidak pakai JOIN ke tabel produk saja? Karena harga bisa berubah kapan saja! Menyimpan harga saat transaksi terjadi adalah praktik standar di e-commerce nyata — historis harga harus terlindungi.
2. Mengisi Data Sampel & Query Dasar
Database kosong seperti toko yang belum punya barang — nggak bisa dicoba. Mari isi data sampel yang realistis, lalu jalankan query dasar untuk memastikan semuanya berjalan dengan benar.
-- Isi kategori INSERT INTO kategori (nama_kategori) VALUES ('Elektronik'), ('Fashion'), ('Aksesoris'); -- Isi produk INSERT INTO produk (nama_produk, harga, stok, id_kategori) VALUES ('Headphone Bluetooth', 599900, 45, 1), ('Smartwatch X200', 850000, 12, 1), ('Kaos Polos Premium', 89000, 200, 2), ('Jaket Hoodie', 349000, 80, 2), ('Case HP Anti-Shock', 45000, 150, 3); -- Isi pelanggan INSERT INTO pelanggan (nama, email, kota) VALUES ('Budi Santoso', 'budi@email.com', 'Jakarta'), ('Sari Dewi', 'sari@email.com', 'Surabaya'), ('Andi Pratama', 'andi@email.com', 'Bandung'), ('Rina Kartika', 'rina@email.com', 'Jakarta'); -- Isi pesanan INSERT INTO pesanan (id_pelanggan, tgl_pesan, status) VALUES (1, '2025-03-01', 'selesai'), (2, '2025-03-05', 'selesai'), (3, '2025-03-10', 'dikirim'), (1, '2025-03-15', 'diproses'), (4, '2025-03-20', 'batal'); -- Isi detail pesanan INSERT INTO detail_pesanan (id_pesanan, id_produk, qty, harga_saat_beli) VALUES (1, 1, 1, 599900), (1, 5, 2, 45000), (2, 3, 3, 89000), (2, 4, 1, 349000), (3, 2, 1, 850000), (3, 1, 1, 599900), (4, 4, 2, 349000), (5, 3, 5, 89000);
📊 Hasil SELECT * FROM produk;
| id_produk | nama_produk | harga | stok | id_kategori |
|---|---|---|---|---|
| 1 | Headphone Bluetooth | 599,900.00 | 45 | 1 |
| 2 | Smartwatch X200 | 850,000.00 | 12 | 1 |
| 3 | Kaos Polos Premium | 89,000.00 | 200 | 2 |
| 4 | Jaket Hoodie | 349,000.00 | 80 | 2 |
| 5 | Case HP Anti-Shock | 45,000.00 | 150 | 3 |
Selalu INSERT tabel induk (parent) sebelum tabel anak (child) yang punya FOREIGN KEY. Urutan yang benar: kategori → produk → pelanggan → pesanan → detail_pesanan. Terbalik sedikit saja, MySQL akan lempar error constraint!
3. Query Analitik Bisnis — Menjawab Pertanyaan Nyata
Inilah bagian yang paling seru dari studi kasus MySQL project: menjawab pertanyaan bisnis yang nyata pakai SQL. Bayangkan kamu adalah data analyst di toko online ini — bosmu minta laporan, dan kamu jawab dengan query.
SELECT p.nama, p.kota, COUNT(DISTINCT ps.id_pesanan) AS total_pesanan, SUM(dp.qty * dp.harga_saat_beli) AS total_belanja FROM pelanggan p JOIN pesanan ps ON p.id_pelanggan = ps.id_pelanggan JOIN detail_pesanan dp ON ps.id_pesanan = dp.id_pesanan WHERE ps.status != 'batal' GROUP BY p.id_pelanggan ORDER BY total_belanja DESC;
📊 Hasil Query:
| nama | kota | total_pesanan | total_belanja |
|---|---|---|---|
| 🥇 Budi Santoso | Jakarta | 2 | Rp 1.387.800 |
| Sari Dewi | Surabaya | 1 | Rp 616.000 |
| Andi Pratama | Bandung | 1 | Rp 1.449.900 |
SELECT pr.nama_produk, k.nama_kategori, SUM(dp.qty) AS total_terjual, SUM(dp.qty * dp.harga_saat_beli) AS total_pendapatan FROM detail_pesanan dp JOIN produk pr ON dp.id_produk = pr.id_produk JOIN kategori k ON pr.id_kategori = k.id_kategori JOIN pesanan ps ON dp.id_pesanan = ps.id_pesanan WHERE ps.status != 'batal' GROUP BY pr.id_produk ORDER BY total_terjual DESC LIMIT 3;
📊 Hasil Query (Top 3):
| nama_produk | nama_kategori | total_terjual | total_pendapatan |
|---|---|---|---|
| 🥇 Kaos Polos Premium | Fashion | 8 | Rp 712.000 |
| Headphone Bluetooth | Elektronik | 2 | Rp 1.199.800 |
| Jaket Hoodie | Fashion | 3 | Rp 1.047.000 |
Selalu filter pesanan dengan status 'batal' saat membuat laporan penjualan. Memasukkan pesanan yang dibatalkan ke dalam laporan akan membuat angka pendapatan dan produk terlaris menjadi tidak akurat — ini kesalahan umum yang sering terjadi pada query analitik pemula.
4. Query Lanjutan: Laporan Penjualan & Stok Kritis
Di level ini, kita gabungkan beberapa konsep sekaligus: JOIN multi-tabel, subquery, dan agregasi. Inilah query-query yang sering diminta di dunia kerja nyata dan tugas akhir kuliah.
SELECT DATE_FORMAT(ps.tgl_pesan, '%Y-%m') AS bulan, COUNT(DISTINCT ps.id_pesanan) AS jumlah_pesanan, SUM(dp.qty * dp.harga_saat_beli) AS total_pendapatan FROM pesanan ps JOIN detail_pesanan dp ON ps.id_pesanan = dp.id_pesanan WHERE ps.status != 'batal' GROUP BY bulan ORDER BY bulan;
| bulan | jumlah_pesanan | total_pendapatan |
|---|---|---|
| 2025-03 | 4 | Rp 3.452.700 |
SELECT pr.nama_produk, k.nama_kategori, pr.stok, CASE WHEN pr.stok = 0 THEN '🔴 Habis' WHEN pr.stok < 10 THEN '🟡 Kritis' ELSE '🟠 Menipis' END AS kondisi_stok FROM produk pr JOIN kategori k ON pr.id_kategori = k.id_kategori WHERE pr.stok < 20 ORDER BY pr.stok ASC;
| nama_produk | nama_kategori | stok | kondisi_stok |
|---|---|---|---|
| Smartwatch X200 | Elektronik | 12 | 🟡 Kritis |
📋 Formula: Pola Query Analitik yang Wajib Kamu Kuasai
SELECT dimension_kolom, -- GROUP BY ini COUNT(*) AS jumlah, -- hitung baris SUM(nilai_kolom) AS total, -- jumlahkan nilai AVG(nilai_kolom) AS rata_rata, -- rata-rata MAX(nilai_kolom) AS tertinggi, -- nilai maks MIN(nilai_kolom) AS terendah -- nilai min FROM tabel_utama JOIN tabel_lain ON kondisi_join -- hubungkan tabel WHERE kondisi_filter -- filter baris dulu GROUP BY dimension_kolom -- kelompokkan HAVING kondisi_grup -- filter setelah GROUP ORDER BY total DESC -- urutkan hasil LIMIT 10; -- batasi output
🚀 Step-by-Step: Bangun Project Database dari Nol
Sebelum buka terminal MySQL, gambar dulu Entity Relationship Diagram-nya. Tentukan tabel, kolom, tipe data, primary key, dan foreign key. Lima menit merancang di kertas bisa menghemat dua jam debugging kemudian.
Jalankan semua perintah CREATE TABLE dari urutan induk ke anak. Verifikasi dengan perintah berikut:
SHOW TABLES; -- tampilkan semua tabel DESCRIBE produk; -- cek struktur tabel
Masukkan data dari tabel tanpa foreign key terlebih dahulu. Setelah selesai, verifikasi dengan:
SELECT COUNT(*) FROM produk; -- harus 5 SELECT COUNT(*) FROM detail_pesanan; -- harus 8
Mulai dari query sederhana (SELECT tanpa JOIN), lalu tambah kompleksitas bertahap. Pahami output setiap query sebelum lanjut ke berikutnya. Jangan copy-paste buta — baca dan mengerti dulu!
Setelah semua query di artikel ini berhasil, coba buat pertanyaan bisnis sendiri dan jawab dengan SQL. Contoh: "Berapa rata-rata nilai pesanan per kota?" atau "Pelanggan mana yang belum pernah memesan bulan ini?" — ini latihan paling efektif!
📊 Ringkasan: Klausa SQL yang Digunakan di Project Ini
| Klausa/Fungsi | Digunakan Untuk | Artikel Referensi |
|---|---|---|
| JOIN ... ON | Menghubungkan data dari beberapa tabel | Artikel 14–16 |
| GROUP BY + SUM/COUNT | Laporan agregasi per dimensi | Artikel 10–11 |
| WHERE ... != | Filter data sebelum agregasi | Artikel 5–6 |
| CASE WHEN ... END | Label kondisional (if-else di SQL) | Artikel 20 |
| DATE_FORMAT() | Format dan kelompokkan data per bulan | Artikel 22 |
| FOREIGN KEY | Integritas relasi antar tabel | Artikel 13 |
| ORDER BY + LIMIT | Ranking & top-N result | Artikel 7–8 |
Query laporan yang sering dipakai? Simpan sebagai VIEW! Contoh: CREATE VIEW v_laporan_bulanan AS SELECT .... Selanjutnya cukup panggil SELECT * FROM v_laporan_bulanan; — jauh lebih praktis dan mudah dibagikan ke rekan tim.
Serius. Studi kasus MySQL project seperti ini — database toko online dengan 5 tabel relasional dan query analitik — sudah setara dengan kualitas tugas besar semester 3-4. Upload ke GitHub, jelaskan di README, dan cantumkan di CV-mu. Banyak perekrut yang menghargai ini lebih dari nilai IPK!
Artikel 27/28: Transaksi MySQL — BEGIN, COMMIT, ROLLBACK
Cara melindungi integritas data dengan mekanisme transaksi dan konsep ACID di MySQL.
Selamat! Kamu Telah Menyelesaikan Seri Ini
28 artikel · MySQL dari Dasar hingga Mahir
No comments:
Post a Comment