Relasi Tabel MySQL: Foreign Key dan JOIN
Pada Bab 4, kita sudah mempelajari operasi CRUD menggunakan INSERT, SELECT, UPDATE, dan DELETE.
Database toko online kita juga sudah mempunyai data produk dan pelanggan.
Tapi ada satu pertanyaan penting.
Bagaimana cara mengetahui pelanggan mana yang membeli produk tertentu?
Kalau semua informasi disimpan dalam satu tabel besar, mungkin awalnya terlihat mudah. Namun, ketika jumlah transaksi bertambah, data bisa menjadi berulang dan semakin sulit dikelola.
Di sinilah relasi antartabel menjadi penting.
Pada Bab 5 Series Database BapakGabut ini, kita akan mempelajari bagaimana tabel saling terhubung menggunakan foreign key, lalu mengambil informasi dari beberapa tabel menggunakan JOIN.
Supaya lebih mudah dipahami, kita akan melanjutkan studi kasus toko online yang sudah dibuat sejak Bab 3.
Kenapa Database Membutuhkan Relasi?
Bayangkan sebuah toko online mempunyai informasi berikut:
| Pelanggan | Produk | Harga | Jumlah |
|---|---|---|---|
| Andi | Keyboard Mechanical | 325000 | 1 |
| Andi | Mouse Wireless | 135000 | 2 |
| Budi | Monitor 24 Inch | 1650000 | 1 |
Sekilas tabel tersebut terlihat cukup sederhana.
Namun, bagaimana jika Andi melakukan sepuluh transaksi?
Nama Andi akan muncul berkali-kali.
Bagaimana jika alamat email Andi berubah?
Jika data pelanggan disalin ke banyak baris transaksi, perubahan informasi bisa menjadi lebih sulit dikelola dan berisiko menimbulkan inkonsistensi.
Database relasional membantu kita memisahkan informasi berdasarkan entitasnya.
Misalnya:
- Tabel
pelangganmenyimpan informasi pelanggan. - Tabel
produkmenyimpan informasi barang. - Tabel
pesananmenyimpan informasi pesanan. - Tabel
detail_pesananmenyimpan daftar produk dalam setiap pesanan.
Keempat tabel tersebut dapat saling terhubung melalui kolom tertentu.
Dengan begitu, informasi pelanggan tidak perlu disalin berulang-ulang ke setiap baris detail transaksi.
Mengenal Primary Key dan Foreign Key
Sebelum membuat relasi, kita perlu memahami dua istilah penting.
Primary Key
Primary key merupakan kolom atau kombinasi kolom yang mengidentifikasi setiap baris secara unik.
Contohnya, tabel pelanggan memiliki:
id_pelanggan INT AUTO_INCREMENT PRIMARY KEYArtinya, setiap pelanggan mempunyai ID yang unik.
Foreign Key
Foreign key merupakan constraint yang menghubungkan kolom pada suatu tabel dengan kolom yang menjadi referensi pada tabel lain, atau pada tabel yang sama.
Contohnya, tabel pesanan memiliki kolom id_pelanggan.
Nilai tersebut mengacu pada id_pelanggan dalam tabel pelanggan.
Dengan foreign key, MySQL dapat membantu menjaga integritas referensial, yaitu memastikan hubungan antardata mengikuti aturan yang ditentukan.
Misalnya, kita tidak bisa begitu saja membuat pesanan dengan ID pelanggan yang tidak tersedia apabila foreign key aktif dan pemeriksaannya diberlakukan.
Mengenal Jenis Relasi Tabel
Ada beberapa pola hubungan yang sering digunakan dalam database relasional.
1. One-to-One
Satu baris pada tabel A berhubungan dengan maksimal satu baris pada tabel B, dan sebaliknya.
Contohnya, satu pengguna mempunyai satu profil tambahan.
Relasi ini biasanya ditegakkan menggunakan foreign key yang juga memiliki aturan UNIQUE, atau menggunakan primary key yang sekaligus menjadi foreign key.
2. One-to-Many
Satu baris pada tabel A dapat berhubungan dengan banyak baris pada tabel B.
Contohnya:
Satu pelanggan dapat mempunyai banyak pesanan.
Namun, setiap pesanan pada desain database kita hanya dimiliki oleh satu pelanggan.
Hubungannya dapat digambarkan sebagai:
pelanggan (1) -------- (N) pesananInilah relasi yang akan kita praktikkan.
3. Many-to-Many
Banyak baris pada tabel A dapat berhubungan dengan banyak baris pada tabel B.
Contohnya:
- Satu pesanan dapat berisi banyak produk.
- Satu produk dapat muncul dalam banyak pesanan.
Hubungan ini biasanya diwujudkan menggunakan tabel penghubung.
Pada studi kasus kita, tabel penghubung tersebut adalah detail_pesanan.
pesanan (1) --- (N) detail_pesanan (N) --- (1) produkJadi, hubungan many-to-many antara pesanan dan produk diimplementasikan melalui dua hubungan one-to-many.
Struktur Database Toko Online Kita
Sampai Bab 4, kita sudah mempunyai dua tabel:
db_toko_bapakgabut||-- pelanggan|-- produkPada bab ini, kita akan menambahkan dua tabel:
db_toko_bapakgabut||-- pelanggan|-- produk|-- pesanan|-- detail_pesananHubungannya adalah:
pelanggan | | 1 : N | pesanan | | 1 : N |detail_pesanan | | N : 1 | produkArtinya, satu pelanggan bisa membuat banyak pesanan, satu pesanan bisa memiliki banyak detail, dan satu produk bisa digunakan dalam banyak detail pesanan.
Persiapan Sebelum Praktik
Buka DBeaver dan hubungkan ke MySQL.
Kemudian jalankan:
USE db_toko_bapakgabut;Pastikan tabel pelanggan dan produk sudah tersedia:
SHOW TABLES;Kita akan menggunakan struktur tabel dari Bab 3 dan data yang sudah dimasukkan pada Bab 4.
Penting: Pada Bab 4, kita sempat mengubah harga dan stok beberapa produk serta menghapus Headset Gaming.
Karena itu, contoh pada bab ini tidak mengasumsikan bahwa seluruh data masih sama persis seperti saat pertama kali dimasukkan.
Kita akan mencari ID berdasarkan nama produk atau nama pelanggan terlebih dahulu.
Jika kamu menjalankan query INSERT dari bab sebelumnya lebih dari sekali, mungkin terdapat data dengan nama yang sama. Periksa data tersebut sebelum melanjutkan.
Untuk mengikuti contoh transaksi dengan mudah, pastikan tersedia masing-masing satu pelanggan bernama Andi dan Budi, serta masing-masing satu produk bernama Keyboard Mechanical, Mouse Wireless, dan Monitor 24 Inch.
Membuat Tabel Pesanan
Tabel pesanan akan menyimpan informasi utama dari sebuah transaksi.
Jalankan query berikut:
CREATE TABLE pesanan ( id_pesanan INT AUTO_INCREMENT PRIMARY KEY, id_pelanggan INT NOT NULL, tanggal_pesanan DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, status_pesanan VARCHAR(20) NOT NULL DEFAULT 'baru', CONSTRAINT fk_pesanan_pelanggan FOREIGN KEY (id_pelanggan) REFERENCES pelanggan(id_pelanggan) ON UPDATE RESTRICT ON DELETE RESTRICT) ENGINE=InnoDB;Mari kita bahas bagian pentingnya.
id_pesanan merupakan primary key tabel pesanan.
id_pelanggan menyimpan referensi pelanggan yang membuat pesanan.
tanggal_pesanan mencatat waktu pembuatan pesanan.
status_pesanan menyimpan status sederhana, misalnya baru, diproses, atau selesai.
Bagian yang membuat relasi adalah:
CONSTRAINT fk_pesanan_pelanggan FOREIGN KEY (id_pelanggan) REFERENCES pelanggan(id_pelanggan)Artinya, kolom id_pelanggan pada tabel pesanan mengacu pada kolom id_pelanggan di tabel pelanggan.
Kita juga menggunakan:
ON UPDATE RESTRICTON DELETE RESTRICTAturan tersebut mencegah perubahan atau penghapusan nilai referensi pada tabel induk jika masih terdapat baris anak yang bergantung padanya.
Untuk contoh toko online, aturan ini membantu menghindari penghapusan pelanggan yang masih memiliki pesanan.
Dalam aplikasi nyata, kebijakan penghapusan data perlu disesuaikan dengan kebutuhan bisnis dan ketentuan penyimpanan data.
Membuat Tabel Detail Pesanan
Selanjutnya, kita membuat tabel untuk menyimpan daftar produk yang dibeli dalam sebuah pesanan.
CREATE TABLE detail_pesanan ( id_detail INT AUTO_INCREMENT PRIMARY KEY, id_pesanan INT NOT NULL, id_produk INT NOT NULL, jumlah INT NOT NULL, harga_satuan DECIMAL(12,2) NOT NULL, CONSTRAINT fk_detail_pesanan FOREIGN KEY (id_pesanan) REFERENCES pesanan(id_pesanan) ON UPDATE RESTRICT ON DELETE RESTRICT, CONSTRAINT fk_detail_produk FOREIGN KEY (id_produk) REFERENCES produk(id_produk) ON UPDATE RESTRICT ON DELETE RESTRICT) ENGINE=InnoDB;Tabel ini memiliki dua foreign key.
Pertama:
FOREIGN KEY (id_pesanan)REFERENCES pesanan(id_pesanan)Kedua:
FOREIGN KEY (id_produk)REFERENCES produk(id_produk)Dengan demikian, setiap baris detail pesanan mengacu pada pesanan dan produk yang tersedia.
Kenapa Harga Satuan Disimpan Lagi?
Mungkin kamu bertanya:
Bukankah harga produk sudah tersedia di tabel produk?
Kenapa kita masih menyimpan harga_satuan di tabel detail_pesanan?
Karena harga produk bisa berubah.
Misalnya, Andi membeli keyboard ketika harganya Rp325.000.
Sebulan kemudian, harga keyboard naik menjadi Rp375.000.
Harga transaksi Andi seharusnya tetap mencerminkan harga saat pembelian, bukan mengikuti harga produk terbaru.
Karena itu, kita menyimpan harga satuan sebagai bagian dari data transaksi.
Ini merupakan contoh sederhana dari penyimpanan informasi historis yang memang diperlukan oleh proses bisnis.
Memahami ON DELETE CASCADE dan RESTRICT
Ketika membuat foreign key, MySQL menyediakan beberapa aturan untuk menangani perubahan atau penghapusan data yang direferensikan.
Dua aturan yang sering dibahas adalah RESTRICT dan CASCADE.
| Aturan | Perilaku |
|---|---|
| RESTRICT | Menolak perubahan atau penghapusan data induk jika masih direferensikan |
| CASCADE | Meneruskan perubahan atau penghapusan key yang direferensikan ke baris anak sesuai operasinya |
Misalnya, jika tabel pesanan menggunakan ON DELETE CASCADE terhadap pelanggan, menghapus pelanggan dapat ikut menghapus pesanan terkait.
Untuk data transaksi, perilaku seperti itu berisiko jika diterapkan tanpa mempertimbangkan kebutuhan bisnis.
Karena itu, pada tutorial ini kita menggunakan RESTRICT.
Ada juga aturan lain seperti SET NULL, tetapi penggunaannya harus sesuai dengan definisi kolom dan desain relasi.
Jangan memilih CASCADE hanya karena terlihat praktis.
Penghapusan data transaksi bukan sesuatu yang sebaiknya terjadi tanpa sengaja.
Menambahkan Data Pesanan
Sekarang kita akan membuat pesanan untuk pelanggan yang sudah ada.
Pertama, cari ID pelanggan:
SELECT id_pelanggan, nama_pelangganFROM pelangganWHERE nama_pelanggan IN ('Andi', 'Budi');Perhatikan hasilnya.
Jika terdapat beberapa pelanggan dengan nama sama, tentukan baris yang benar berdasarkan informasi lainnya.
Untuk contoh berikut, kita menggunakan variabel sesi MySQL agar tidak bergantung pada angka AUTO_INCREMENT tertentu.
Jalankan:
SET @id_andi = ( SELECT id_pelanggan FROM pelanggan WHERE email = 'andi@example.com' LIMIT 1);
SET @id_budi = ( SELECT id_pelanggan FROM pelanggan WHERE email = 'budi@example.com' LIMIT 1);Variabel dengan awalan @ merupakan variabel sesi MySQL.
Nilainya dapat digunakan kembali selama sesi koneksi tersebut masih berlaku.
Pada contoh ini, LIMIT 1 digunakan agar subquery menghasilkan maksimal satu nilai. Namun, jika ada beberapa pelanggan dengan email yang sama, hasilnya tidak boleh dianggap sebagai identifikasi yang andal.
Pastikan email contoh tersebut hanya dimiliki oleh satu pelanggan.
Periksa variabel:
SELECT @id_andi AS id_andi, @id_budi AS id_budi;Jika salah satu nilainya NULL, berarti pelanggan yang dicari tidak ditemukan.
Jangan lanjutkan INSERT sebelum masalah tersebut diperbaiki.
Sekarang buat pesanan:
INSERT INTO pesanan (id_pelanggan, status_pesanan)VALUES (@id_andi, 'baru'), (@id_budi, 'baru');Kita sudah membuat dua pesanan.
Satu untuk Andi dan satu untuk Budi.
Menemukan ID Pesanan
Karena ID pesanan dihasilkan otomatis, kita perlu memeriksanya sebelum menambahkan detail.
SELECT p.id_pesanan, p.id_pelanggan, p.tanggal_pesanan, p.status_pesananFROM pesanan pORDER BY p.id_pesanan DESC;Catat ID pesanan yang baru dibuat.
Untuk contoh berikut, kita akan menggunakan variabel @pesanan_andi dan @pesanan_budi.
Isi variabel tersebut menggunakan ID yang benar dari hasil query sebelumnya.
Misalnya, jika pesanan Andi mempunyai ID 1 dan pesanan Budi mempunyai ID 2:
SET @pesanan_andi = 1;SET @pesanan_budi = 2;Angka di atas hanya contoh.
Jika hasil di database kamu berbeda, ganti dengan ID yang sebenarnya.
Menambahkan Detail Pesanan
Sekarang kita cari ID produk.
SELECT id_produk, nama_produk, hargaFROM produkWHERE nama_produk IN ( 'Keyboard Mechanical', 'Mouse Wireless', 'Monitor 24 Inch');Pastikan setiap nama produk mengarah pada produk yang dimaksud.
Untuk mempermudah latihan, kita akan mengambil ID produk melalui variabel.
SET @produk_keyboard = ( SELECT id_produk FROM produk WHERE nama_produk = 'Keyboard Mechanical' LIMIT 1);
SET @produk_mouse = ( SELECT id_produk FROM produk WHERE nama_produk = 'Mouse Wireless' LIMIT 1);
SET @produk_monitor = ( SELECT id_produk FROM produk WHERE nama_produk = 'Monitor 24 Inch' LIMIT 1);Periksa hasilnya:
SELECT @produk_keyboard AS id_keyboard, @produk_mouse AS id_mouse, @produk_monitor AS id_monitor;Jika semua ID tersedia, sekarang kita bisa menambahkan detail pesanan.
Kita akan membuat skenario:
- Andi membeli satu Keyboard Mechanical dan dua Mouse Wireless.
- Budi membeli satu Monitor 24 Inch.
Jalankan:
INSERT INTO detail_pesanan ( id_pesanan, id_produk, jumlah, harga_satuan)VALUES (@pesanan_andi, @produk_keyboard, 1, 325000), (@pesanan_andi, @produk_mouse, 2, 135000), (@pesanan_budi, @produk_monitor, 1, 1650000);Angka harga pada contoh ini merupakan harga transaksi simulasi yang kita tentukan sendiri.
Jadi, nilainya tidak harus sama dengan harga produk saat ini.
Pada aplikasi nyata, backend biasanya mengambil harga yang berlaku dari sumber tepercaya, menerapkan aturan diskon jika ada, lalu menyimpan harga transaksi secara konsisten.
Catatan: Jalankan INSERT contoh pesanan dan detail ini satu kali saja. Jika diulang, kamu dapat membuat transaksi duplikat.
Mengenal JOIN dalam SQL
Sekarang kita sudah mempunyai empat tabel yang saling berhubungan.
Namun, bagaimana cara menampilkan nama pelanggan dari tabel pelanggan bersama informasi pesanan dari tabel pesanan?
Di sinilah kita menggunakan JOIN.
JOIN memungkinkan kita menggabungkan informasi dari beberapa tabel berdasarkan kondisi hubungan tertentu.
Jenis JOIN yang akan kita pelajari:
- INNER JOIN
- LEFT JOIN
- RIGHT JOIN
Kita juga akan melihat bagaimana menggabungkan lebih dari dua tabel.
INNER JOIN — Mengambil Data yang Memiliki Pasangan
INNER JOIN hanya mengembalikan kombinasi baris yang memenuhi kondisi JOIN.
Misalnya, kita ingin menampilkan pesanan beserta nama pelanggannya.
SELECT p.id_pesanan, pl.nama_pelanggan, p.tanggal_pesanan, p.status_pesananFROM pesanan pINNER JOIN pelanggan pl ON p.id_pelanggan = pl.id_pelanggan;Perhatikan bagian:
FROM pesanan pINNER JOIN pelanggan pl ON p.id_pelanggan = pl.id_pelangganp merupakan alias untuk tabel pesanan.
pl merupakan alias untuk tabel pelanggan.
Alias membantu kita menulis query lebih ringkas, terutama ketika beberapa tabel memiliki nama kolom yang sama.
Kondisi ON menentukan bagaimana kedua tabel dihubungkan.
Jika setiap pesanan memiliki pelanggan yang valid, hasilnya akan menampilkan pesanan bersama nama pelanggan.
Kenapa Menggunakan Alias?
Tanpa alias, kita perlu menulis nama tabel secara lengkap:
SELECT pesanan.id_pesanan, pelanggan.nama_pelangganFROM pesananINNER JOIN pelanggan ON pesanan.id_pelanggan = pelanggan.id_pelanggan;Query tersebut tetap benar.
Namun, ketika jumlah tabel bertambah, alias membuat query lebih mudah dibaca.
LEFT JOIN — Tetap Menampilkan Data dari Tabel Kiri
Berbeda dengan INNER JOIN, LEFT JOIN mempertahankan seluruh baris dari tabel di sebelah kiri JOIN.
Jika tidak ditemukan pasangan di tabel kanan, kolom dari tabel kanan akan bernilai NULL.
Contohnya, kita ingin melihat semua pelanggan, termasuk pelanggan yang belum pernah membuat pesanan.
SELECT pl.id_pelanggan, pl.nama_pelanggan, p.id_pesanan, p.status_pesananFROM pelanggan plLEFT JOIN pesanan p ON pl.id_pelanggan = p.id_pelangganORDER BY pl.id_pelanggan, p.id_pesanan;Pada contoh ini, tabel kiri adalah pelanggan.
Artinya, seluruh pelanggan tetap ditampilkan.
Jika Citra dan Dina belum mempunyai pesanan, keduanya tetap muncul dengan informasi pesanan bernilai NULL.
Jika satu pelanggan memiliki beberapa pesanan, nama pelanggan tersebut dapat muncul lebih dari satu kali.
Itu bukan berarti JOIN salah.
Itu terjadi karena satu pelanggan memang mempunyai hubungan one-to-many dengan pesanan.
Mencari Pelanggan yang Belum Pernah Memesan
LEFT JOIN juga dapat digunakan untuk mencari baris yang belum mempunyai pasangan.
SELECT pl.id_pelanggan, pl.nama_pelangganFROM pelanggan plLEFT JOIN pesanan p ON pl.id_pelanggan = p.id_pelangganWHERE p.id_pesanan IS NULL;Query tersebut mencari pelanggan yang tidak mempunyai pesanan.
Perhatikan penggunaan:
IS NULLDalam SQL, kita menggunakan IS NULL untuk memeriksa nilai NULL, bukan = NULL.
RIGHT JOIN — Mempertahankan Data dari Tabel Kanan
MySQL juga mendukung RIGHT JOIN.
Konsepnya mirip dengan LEFT JOIN, tetapi tabel yang seluruh barisnya dipertahankan berada di sebelah kanan.
Contoh:
SELECT pl.nama_pelanggan, p.id_pesananFROM pesanan pRIGHT JOIN pelanggan pl ON p.id_pelanggan = pl.id_pelanggan;Query tersebut mempertahankan seluruh pelanggan.
Dalam banyak kasus, RIGHT JOIN dapat ditulis ulang menjadi LEFT JOIN dengan menukar posisi tabel.
Karena itu, untuk konsistensi dan kemudahan membaca query, banyak pengembang lebih sering menggunakan LEFT JOIN.
Menggabungkan Empat Tabel Sekaligus
Sekarang kita masuk ke contoh yang lebih menarik.
Kita ingin menampilkan informasi:
- Nomor pesanan.
- Nama pelanggan.
- Nama produk.
- Jumlah pembelian.
- Harga satuan.
- Subtotal.
Informasi tersebut tersebar di empat tabel.
Gunakan query berikut:
SELECT p.id_pesanan, pl.nama_pelanggan, pr.nama_produk, dp.jumlah, dp.harga_satuan, dp.jumlah * dp.harga_satuan AS subtotalFROM pesanan pINNER JOIN pelanggan pl ON p.id_pelanggan = pl.id_pelangganINNER JOIN detail_pesanan dp ON p.id_pesanan = dp.id_pesananINNER JOIN produk pr ON dp.id_produk = pr.id_produkORDER BY p.id_pesanan, dp.id_detail;Mari kita pahami alurnya.
Pertama, query mengambil data dari tabel pesanan.
Kedua, menghubungkannya dengan tabel pelanggan untuk mendapatkan nama pelanggan.
Ketiga, menghubungkan pesanan dengan detail_pesanan untuk mengetahui produk dan jumlah yang dibeli.
Keempat, menghubungkan detail dengan tabel produk untuk mendapatkan nama produk.
Hasilnya kurang lebih seperti berikut, jika data latihan sesuai skenario:
| Pesanan | Pelanggan | Produk | Jumlah | Harga Satuan | Subtotal |
|---|---|---|---|---|---|
| 1 | Andi | Keyboard Mechanical | 1 | 325000 | 325000 |
| 1 | Andi | Mouse Wireless | 2 | 135000 | 270000 |
| 2 | Budi | Monitor 24 Inch | 1 | 1650000 | 1650000 |
Nomor pesanan yang sebenarnya mengikuti nilai AUTO_INCREMENT pada database masing-masing.
Perhatikan bagian:
dp.jumlah * dp.harga_satuan AS subtotalMySQL menghitung subtotal dengan mengalikan jumlah barang dan harga satuan.
AS subtotal memberikan alias pada kolom hasil perhitungan.
Dengan query seperti ini, data dari empat tabel dapat ditampilkan menjadi satu laporan transaksi yang lebih mudah dibaca.
Menghitung Total Setiap Pesanan
Setelah bisa menghitung subtotal, kita dapat menjumlahkannya berdasarkan pesanan.
Gunakan SUM dan GROUP BY:
SELECT p.id_pesanan, pl.nama_pelanggan, SUM(dp.jumlah * dp.harga_satuan) AS total_pesananFROM pesanan pINNER JOIN pelanggan pl ON p.id_pelanggan = pl.id_pelangganINNER JOIN detail_pesanan dp ON p.id_pesanan = dp.id_pesananGROUP BY p.id_pesanan, pl.nama_pelangganORDER BY p.id_pesanan;Jika mengikuti contoh transaksi sebelumnya, hasil perhitungannya:
| Pelanggan | Total Pesanan |
|---|---|
| Andi | 595000 |
| Budi | 1650000 |
Perhitungannya:
Andi:
(1 × 325000) + (2 × 135000) = 595000Budi:
1 × 1650000 = 1650000SUM digunakan untuk menjumlahkan nilai.
GROUP BY digunakan untuk mengelompokkan baris berdasarkan kolom tertentu.
Kita baru mengenalkannya secara singkat di sini.
Pada Bab 6, kita akan membahas GROUP BY, HAVING, dan subquery dengan lebih mendalam.
Kesalahan Umum Saat Menggunakan JOIN
JOIN merupakan fitur SQL yang sangat berguna, tetapi ada beberapa kesalahan yang perlu dihindari.
1. Salah Menentukan Kolom Relasi
Misalnya, menghubungkan id_pelanggan dengan id_produk hanya karena keduanya bertipe INT.
Tipe data yang sama tidak berarti kedua kolom mempunyai hubungan.
JOIN harus mengikuti hubungan data yang benar.
2. Lupa Kondisi JOIN
Jika kita menggabungkan tabel tanpa kondisi hubungan yang sesuai, hasilnya dapat menjadi perkalian kombinasi baris.
Contohnya:
SELECT *FROM pelangganCROSS JOIN produk;Query tersebut menghasilkan kombinasi setiap pelanggan dengan setiap produk.
CROSS JOIN memang berguna untuk kebutuhan tertentu, tetapi dapat menghasilkan jumlah baris sangat besar.
Jangan menggunakannya ketika yang dibutuhkan sebenarnya adalah relasi berdasarkan foreign key.
3. Menganggap Hasil JOIN Selalu Satu Baris
Pada relasi one-to-many, satu baris induk dapat memiliki banyak baris anak.
Karena itu, satu pelanggan bisa muncul berkali-kali ketika JOIN dengan tabel pesanan.
Hal tersebut normal.
Jika ingin satu baris per pelanggan, kita perlu mempertimbangkan agregasi atau bentuk query lain sesuai kebutuhan.
4. Salah Menempatkan Kondisi pada LEFT JOIN
Perhatikan contoh:
SELECT pl.nama_pelanggan, p.id_pesananFROM pelanggan plLEFT JOIN pesanan p ON pl.id_pelanggan = p.id_pelangganWHERE p.status_pesanan = 'baru';Kondisi pada WHERE tersebut akan menghilangkan baris pelanggan yang tidak mempunyai pesanan karena nilai p.status_pesanan adalah NULL.
Jika tujuan kita mempertahankan semua pelanggan sambil hanya menggabungkan pesanan berstatus baru, letakkan kondisi pada ON:
SELECT pl.nama_pelanggan, p.id_pesananFROM pelanggan plLEFT JOIN pesanan p ON pl.id_pelanggan = p.id_pelanggan AND p.status_pesanan = 'baru';Perbedaan penempatan kondisi ini dapat mengubah hasil query secara signifikan.
Latihan Mandiri
Sekarang waktunya mencoba sendiri.
Gunakan database db_toko_bapakgabut yang sudah mempunyai empat tabel.
Latihan 1 — JOIN Dua Tabel
Buat query untuk menampilkan:
- ID pesanan.
- Nama pelanggan.
- Tanggal pesanan.
Gunakan INNER JOIN.
Latihan 2 — Pelanggan Tanpa Pesanan
Buat query untuk menampilkan pelanggan yang belum pernah melakukan pemesanan.
Gunakan LEFT JOIN.
Latihan 3 — Detail Pembelian
Buat query untuk menampilkan:
- Nama pelanggan.
- Nama produk.
- Jumlah pembelian.
- Harga satuan.
- Subtotal.
Gunakan JOIN antartabel yang diperlukan.
Latihan 4 — Total Pembelian per Pelanggan
Buat query untuk menghitung total nilai pembelian masing-masing pelanggan yang sudah memiliki detail pesanan.
Gunakan SUM dan GROUP BY.
Coba kerjakan terlebih dahulu sebelum melihat pembahasannya.
Pembahasan Latihan
Jawaban Latihan 1
SELECT p.id_pesanan, pl.nama_pelanggan, p.tanggal_pesananFROM pesanan pINNER JOIN pelanggan pl ON p.id_pelanggan = pl.id_pelanggan;Jawaban Latihan 2
SELECT pl.id_pelanggan, pl.nama_pelangganFROM pelanggan plLEFT JOIN pesanan p ON pl.id_pelanggan = p.id_pelangganWHERE p.id_pesanan IS NULL;Jawaban Latihan 3
SELECT pl.nama_pelanggan, pr.nama_produk, dp.jumlah, dp.harga_satuan, dp.jumlah * dp.harga_satuan AS subtotalFROM detail_pesanan dpINNER JOIN pesanan p ON dp.id_pesanan = p.id_pesananINNER JOIN pelanggan pl ON p.id_pelanggan = pl.id_pelangganINNER JOIN produk pr ON dp.id_produk = pr.id_produk;Jawaban Latihan 4
SELECT pl.id_pelanggan, pl.nama_pelanggan, SUM(dp.jumlah * dp.harga_satuan) AS total_pembelianFROM pelanggan plINNER JOIN pesanan p ON pl.id_pelanggan = p.id_pelangganINNER JOIN detail_pesanan dp ON p.id_pesanan = dp.id_pesananGROUP BY pl.id_pelanggan, pl.nama_pelangganORDER BY total_pembelian DESC;Query tersebut menampilkan pelanggan yang memiliki detail pembelian, kemudian mengurutkannya berdasarkan total pembelian terbesar.
Jika ingin tetap menampilkan pelanggan yang belum pernah membeli dengan total nol, kita perlu menyesuaikan query menggunakan LEFT JOIN dan penanganan nilai NULL.
Kesimpulan
Pada Bab 5 ini, kita sudah mempelajari konsep penting dalam database relasional.
Primary key digunakan untuk mengidentifikasi baris secara unik.
Foreign key digunakan untuk menjaga hubungan referensial antartabel.
Relasi one-to-many memungkinkan satu baris induk memiliki banyak baris anak.
Relasi many-to-many dapat diimplementasikan melalui tabel penghubung.
Kita juga sudah mempelajari bagaimana INNER JOIN, LEFT JOIN, dan RIGHT JOIN bekerja.
Dengan JOIN, informasi yang tersimpan pada beberapa tabel dapat digabungkan menjadi laporan yang lebih bermakna.
Database toko online kita sekarang memiliki empat tabel yang saling terhubung:
pelangganprodukpesanandetail_pesanan
Pada Bab 6, kita akan mempelajari query SQL lanjutan menggunakan GROUP BY, HAVING, dan subquery.
Kita akan mencoba membuat laporan penjualan, menghitung total transaksi, dan mengambil informasi berdasarkan hasil query lainnya.
Karena setelah toko online berhasil mencatat transaksi, tentu pemilik toko ingin tahu berapa banyak barang yang terjual dan siapa pelanggan yang paling rajin belanja.
Sampai jumpa di artikel berikutnya, Gabuters!
Bagikan Artikel
Buat poster berbagi atau salin tautan artikel ini.
Lanjut membaca
Coba jalur lain
Pilihan konsisten dari artikel lain
Terakhir diperbarui pada , 20 hari yang lalu
Sebagian konten mungkin sudah tidak berlaku