Di dua episode sebelumnya kamu bekerja dengan satu tabel. Kenyataannya, aplikasi nyata menyimpan data di banyak tabel: tabel pelanggan terpisah dari tabel pesanan, tabel siswa terpisah dari tabel nilai. Kenapa dipecah? Supaya tidak ada data yang ditulis berulang-ulang (kamu akan mendalami alasannya di episode #67 tentang desain database).
Konsekuensinya, kamu butuh cara untuk menggabungkan kembali tabel-tabel itu saat membaca data. Misalnya: "tampilkan nama pelanggan beserta total pesanannya". Nama ada di tabel pelanggan, total ada di tabel pesanan. Perintah SQL untuk menggabungkannya disebut JOIN.
Analoginya seperti dua buku catatan kasir: satu buku berisi daftar nama pelanggan, satu buku berisi daftar transaksi. JOIN adalah kegiatan menyandingkan kedua buku itu berdampingan, mencocokkan baris yang merujuk ke orang yang sama. Di akhir artikel ini kamu akan menguasai empat jenis JOIN utama, memahami kenapa ON dan WHERE itu berbeda, dan bisa melakukan trik keren: menggabungkan tabel dengan dirinya sendiri.
Kenapa Data Dipecah Jadi Banyak Tabel
Bayangkan kalau semua data pelanggan dan pesanan disimpan dalam satu tabel raksasa. Setiap kali Budi memesan, nama dan alamat Budi ditulis ulang. Kalau Budi pindah alamat, kamu harus mengubah semua baris pesanannya satu per satu. Lupa satu saja, datanya jadi tidak konsisten.
Solusinya: pecah jadi dua tabel dan hubungkan dengan foreign key, persis seperti yang kamu pelajari di episode #63:
-- tabel pelanggan
-- id | nama
-- 1 | Budi
-- 2 | Siti
-- 3 | Andi
-- tabel pesanan
-- id | pelanggan_id | produk | total
-- 101 | 1 | Kopi | 15000
-- 102 | 1 | Teh | 5000
-- 103 | 2 | Roti | 10000
Kolom pelanggan_id di tabel pesanan adalah foreign key yang merujuk ke id di tabel pelanggan. Perhatikan: Budi (id 1) punya dua pesanan, Siti (id 2) punya satu, dan Andi (id 3) belum pernah memesan apa pun. Detail kecil ini akan penting sebentar lagi.
INNER JOIN: Hanya yang Saling Cocok
INNER JOIN menggabungkan dua tabel dan hanya menampilkan baris yang pasangannya ketemu di kedua sisi:
SELECT pelanggan.nama, pesanan.produk, pesanan.total
FROM pelanggan
INNER JOIN pesanan ON pelanggan.id = pesanan.pelanggan_id;
Bagian ON adalah aturan pencocokannya: baris pelanggan dengan id sekian dipasangkan dengan baris pesanan yang pelanggan_id-nya sama. Hasilnya tiga baris: Budi-Kopi, Budi-Teh, Siti-Roti.
Berhenti dan tebak dulu! Dengan data di atas, apakah nama Andi muncul di hasil INNER JOIN? Kenapa?
SELECT pelanggan.nama, pesanan.produk
FROM pelanggan
INNER JOIN pesanan ON pelanggan.id = pesanan.pelanggan_id;
Catatan: kalau kamu menulis JOIN saja tanpa kata INNER, artinya tetap INNER JOIN. Ini meluruskan mitos pertama: "JOIN cuma ada satu macam". Kata JOIN polos memang berarti INNER, tapi itu hanya satu dari keluarga besar JOIN.
LEFT JOIN: Tabel Kiri Dipertahankan
Bagaimana kalau kamu ingin daftar semua pelanggan, termasuk yang belum pernah memesan? Di sinilah LEFT JOIN berperan. Ia mengambil semua baris dari tabel kiri (tabel yang ditulis di FROM), lalu menempelkan pasangannya dari tabel kanan kalau ada:
SELECT pelanggan.nama, pesanan.produk
FROM pelanggan
LEFT JOIN pesanan ON pelanggan.id = pesanan.pelanggan_id;
Hasilnya empat baris. Budi dan Siti tampil seperti sebelumnya, dan Andi ikut tampil dengan kolom produk yang kosong (NULL). NULL di sini artinya jujur: "tidak ada data", bukan nol, bukan teks kosong.
Berhenti dan tebak dulu! Apa isi kolom produk untuk baris Andi pada query LEFT JOIN di atas? Dan bagaimana cara menampilkan hanya pelanggan yang belum pernah memesan?
SELECT pelanggan.nama, pesanan.produk
FROM pelanggan
LEFT JOIN pesanan ON pelanggan.id = pesanan.pelanggan_id;
RIGHT JOIN dan FULL JOIN Sekilas
Dua saudara lainnya jarang dipakai tapi perlu dikenal. RIGHT JOIN adalah kebalikan LEFT JOIN: semua baris tabel kanan dipertahankan. Praktiknya, programmer hampir selalu menukar posisi tabel dan tetap memakai LEFT JOIN, karena membaca "tabel kiri dipertahankan" lebih natural daripada "tabel kanan".
FULL JOIN (atau FULL OUTER JOIN) mempertahankan semua baris dari kedua tabel, menempelkan yang cocok dan mengisi NULL untuk yang tidak cocok. Berguna untuk rekonsiliasi, misalnya membandingkan daftar pelanggan di dua sistem yang berbeda. Catatan: MySQL tidak mendukung FULL JOIN secara langsung, jadi topik ini cukup dikenal saja untuk sekarang.
Self JOIN: Tabel Menggabungkan Dirinya Sendiri
Kasus spesial yang terlihat mustahil: bagaimana kalau kolom rujukannya menunjuk ke tabel yang sama? Contoh klasik: tabel pegawai, di mana setiap pegawai punya atasan yang juga pegawai:
-- tabel pegawai
-- id | nama | atasan_id
-- 1 | Andi | NULL
-- 2 | Budi | 1
-- 3 | Citra | 1
-- 4 | Dedi | 2
Kolom atasan_id merujuk ke id di tabel yang sama. Budi atasannya Andi, Dedi atasannya Budi, Andi tidak punya atasan. Untuk menampilkan nama pegawai beserta nama atasannya, kita "menggandakan" tabel dengan dua alias berbeda:
SELECT e.nama AS pegawai, m.nama AS atasan
FROM pegawai AS e
LEFT JOIN pegawai AS m ON e.atasan_id = m.id;
Alias e berperan sebagai "pegawai", alias m berperan sebagai "atasan". Tanpa alias, database akan bingung: nama yang mana, dari salinan yang mana? Hasilnya: Budi-Andi, Citra-Andi, Dedi-Budi, dan Andi dengan atasan NULL. Dipakainya LEFT JOIN supaya Andi si bos besar tetap tampil walau tidak punya atasan.
Ini juga meluruskan mitos kedua: "ON dan WHERE itu sama saja". Keduanya memang sama-sama kondisi, tapi perannya beda. ON menentukan cara menggabungkan dua tabel, sedangkan WHERE menyaring hasil yang sudah digabung. Buktinya: kalau kamu memindahkan kondisi ke WHERE pada LEFT JOIN, misalnya WHERE pesanan.total > 10000, baris Andi yang NULL ikut gugur karena NULL tidak lolos perbandingan, dan LEFT JOIN-mu diam-diam berubah rasa jadi INNER JOIN. Pahami bedanya, dan kamu terhindar dari bug yang sulit dilacak.
Kuis Cepat
Kuis cepat: Kamu diminta membuat laporan "semua siswa beserta nilainya, termasuk siswa yang belum punya nilai". JOIN apa yang tepat?
Latihan: Laporan Warung Mini
Buat dua tabel ini di MySQL-mu, lalu kerjakan tantangannya:
CREATE TABLE pelanggan (
id INT AUTO_INCREMENT PRIMARY KEY,
nama VARCHAR(100) NOT NULL
);
CREATE TABLE pesanan (
id INT AUTO_INCREMENT PRIMARY KEY,
pelanggan_id INT NOT NULL,
produk VARCHAR(100) NOT NULL,
total INT NOT NULL,
FOREIGN KEY (pelanggan_id) REFERENCES pelanggan(id)
);
- Isi tabel pelanggan dengan 4 nama, dan tabel pesanan dengan 5-6 baris. Sengaja buat satu pelanggan tanpa pesanan dan satu pesanan dengan total di bawah 10000.
- Tulis INNER JOIN untuk menampilkan nama pelanggan dan produknya. Hitung baris hasilnya, cocokkan dengan tebakanmu.
- Ubah query nomor 2 menjadi LEFT JOIN. Perhatikan baris baru yang muncul dan nilai NULL-nya.
- Tulis query untuk "pelanggan yang belum pernah memesan" memakai pola LEFT JOIN plus
IS NULL. - Tantangan: buat tabel
karyawan(id, nama, manajer_id) berisi 5 orang dengan struktur atasan-bawahan versimu, lalu tulis SELF JOIN untuk menampilkan setiap karyawan beserta nama manajernya. - Eksperimen ON vs WHERE: pada LEFT JOIN nomor 3, tambahkan
WHERE pesanan.total > 8000dan amati apa yang terjadi pada pelanggan tanpa pesanan. Tulis kesimpulanmu satu kalimat.
Di episode #67, Cara Mendesain Database untuk Aplikasi Web, kita menutup seri Database dan SQL. Kamu akan belajar merancang tabel dari nol: menentukan entitas, memilih primary key, menghubungkan tabel dengan relasi yang tepat, dan merapikan desain dengan normalisasi. Bekal wajib sebelum masuk ke dunia framework di episode #68.
Diskusi & Komentar 0
Punya tanggapan, pertanyaan, atau tips seputar tutorial ini? Tulis komentar kamu di bawah!
Jadilah yang pertama membuka obrolan dan berbagi pandangan seputar tutorial resep kode ini.
Tulis Komentar
Komentar menggunakan akun Google. Harap berkomentar dengan santun dan berbobot.