2825 kata
14 menit

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:

PelangganProdukHargaJumlah
AndiKeyboard Mechanical3250001
AndiMouse Wireless1350002
BudiMonitor 24 Inch16500001

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 pelanggan menyimpan informasi pelanggan.
  • Tabel produk menyimpan informasi barang.
  • Tabel pesanan menyimpan informasi pesanan.
  • Tabel detail_pesanan menyimpan 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 KEY

Artinya, 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) pesanan

Inilah 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) produk

Jadi, 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
|-- produk

Pada bab ini, kita akan menambahkan dua tabel:

db_toko_bapakgabut
|
|-- pelanggan
|-- produk
|-- pesanan
|-- detail_pesanan

Hubungannya adalah:

pelanggan
|
| 1 : N
|
pesanan
|
| 1 : N
|
detail_pesanan
|
| N : 1
|
produk

Artinya, 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 RESTRICT
ON DELETE RESTRICT

Aturan 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.

AturanPerilaku
RESTRICTMenolak perubahan atau penghapusan data induk jika masih direferensikan
CASCADEMeneruskan 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_pelanggan
FROM pelanggan
WHERE 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_pesanan
FROM pesanan p
ORDER 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, harga
FROM produk
WHERE 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_pesanan
FROM pesanan p
INNER JOIN pelanggan pl
ON p.id_pelanggan = pl.id_pelanggan;

Perhatikan bagian:

FROM pesanan p
INNER JOIN pelanggan pl
ON p.id_pelanggan = pl.id_pelanggan

p 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_pelanggan
FROM pesanan
INNER 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_pesanan
FROM pelanggan pl
LEFT JOIN pesanan p
ON pl.id_pelanggan = p.id_pelanggan
ORDER 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_pelanggan
FROM pelanggan pl
LEFT JOIN pesanan p
ON pl.id_pelanggan = p.id_pelanggan
WHERE p.id_pesanan IS NULL;

Query tersebut mencari pelanggan yang tidak mempunyai pesanan.

Perhatikan penggunaan:

IS NULL

Dalam 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_pesanan
FROM pesanan p
RIGHT 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 subtotal
FROM pesanan p
INNER JOIN pelanggan pl
ON p.id_pelanggan = pl.id_pelanggan
INNER JOIN detail_pesanan dp
ON p.id_pesanan = dp.id_pesanan
INNER JOIN produk pr
ON dp.id_produk = pr.id_produk
ORDER 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:

PesananPelangganProdukJumlahHarga SatuanSubtotal
1AndiKeyboard Mechanical1325000325000
1AndiMouse Wireless2135000270000
2BudiMonitor 24 Inch116500001650000

Nomor pesanan yang sebenarnya mengikuti nilai AUTO_INCREMENT pada database masing-masing.

Perhatikan bagian:

dp.jumlah * dp.harga_satuan AS subtotal

MySQL 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_pesanan
FROM pesanan p
INNER JOIN pelanggan pl
ON p.id_pelanggan = pl.id_pelanggan
INNER JOIN detail_pesanan dp
ON p.id_pesanan = dp.id_pesanan
GROUP BY
p.id_pesanan,
pl.nama_pelanggan
ORDER BY p.id_pesanan;

Jika mengikuti contoh transaksi sebelumnya, hasil perhitungannya:

PelangganTotal Pesanan
Andi595000
Budi1650000

Perhitungannya:

Andi:

(1 × 325000) + (2 × 135000) = 595000

Budi:

1 × 1650000 = 1650000

SUM 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 pelanggan
CROSS 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_pesanan
FROM pelanggan pl
LEFT JOIN pesanan p
ON pl.id_pelanggan = p.id_pelanggan
WHERE 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_pesanan
FROM pelanggan pl
LEFT 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_pesanan
FROM pesanan p
INNER JOIN pelanggan pl
ON p.id_pelanggan = pl.id_pelanggan;

Jawaban Latihan 2#

SELECT
pl.id_pelanggan,
pl.nama_pelanggan
FROM pelanggan pl
LEFT JOIN pesanan p
ON pl.id_pelanggan = p.id_pelanggan
WHERE 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 subtotal
FROM detail_pesanan dp
INNER JOIN pesanan p
ON dp.id_pesanan = p.id_pesanan
INNER JOIN pelanggan pl
ON p.id_pelanggan = pl.id_pelanggan
INNER 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_pembelian
FROM pelanggan pl
INNER JOIN pesanan p
ON pl.id_pelanggan = p.id_pelanggan
INNER JOIN detail_pesanan dp
ON p.id_pesanan = dp.id_pesanan
GROUP BY
pl.id_pelanggan,
pl.nama_pelanggan
ORDER 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:

  • pelanggan
  • produk
  • pesanan
  • detail_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!

Bagian 5 dari 8Tulisan ini bagian dari seri

Bagikan Artikel

Buat poster berbagi atau salin tautan artikel ini.

Lanjut membaca

Bacaan terkait

Berdasarkan tag dan kategori yang sama

Coba jalur lain

Pilihan konsisten dari artikel lain