Bab 12: JOIN (Menggabungkan Data dari Banyak Tabel)

Apa yang Akan Kamu Pahami Setelah Bab Ini
  • Notasi Crow's Foot pada ERD (Entity Relationship Diagram) untuk membaca relasi data.
  • Empat jenis JOIN utama: INNER JOIN, LEFT JOIN, RIGHT JOIN, dan FULL OUTER JOIN.
  • Penanganan nilai NULL hasil LEFT/RIGHT JOIN menggunakan fungsi COALESCE.
  • Teknik mengombinasikan 3 tabel atau lebih secara berurutan dalam satu query SQL.

Kenapa JOIN Dibutuhkan

Pada Bab 9, kamu sudah mempelajari bahwa database relasional sengaja memisahkan data ke dalam beberapa tabel berbeda, seperti tabel pelanggan, produk, dan pesanan pada Belanjain, untuk menghindari redundansi data. Namun pemisahan ini menimbulkan konsekuensi baru, yaitu ketika kamu ingin melihat informasi yang lengkap, misalnya nama pelanggan sekaligus nama produk yang mereka pesan, informasi tersebut sebenarnya tersebar di beberapa tabel berbeda, bukan berada dalam satu tabel yang sama.

JOIN adalah perintah SQL yang memungkinkan kamu menggabungkan baris-baris dari dua tabel atau lebih menjadi satu hasil tampilan, berdasarkan kolom penghubung yang sama, biasanya berupa relasi Primary Key dan Foreign Key. Tanpa JOIN, kamu hanya bisa melihat data pesanan dalam bentuk ID Pelanggan dan ID Produk yang berupa angka tanpa makna, namun dengan JOIN, angka-angka tersebut bisa "diterjemahkan" menjadi nama pelanggan dan nama produk yang sebenarnya.

Membaca ERD dengan Notasi Crow's Foot

Sebelum masuk ke penulisan query JOIN, penting memahami cara membaca ERD, yaitu diagram visual yang menggambarkan struktur tabel dalam sebuah database beserta relasi antar tabelnya. Notasi crow's foot (kaki gagak) adalah cara paling umum untuk menggambarkan kardinalitas antar tabel, dinamai demikian karena simbol yang dipakai untuk menandakan hubungan "banyak" terlihat seperti kaki burung gagak yang bercabang tiga.

Pada database Belanjain, garis yang menghubungkan PELANGGAN dan PESANAN menunjukkan relasi satu ke banyak (sesuai pembahasan kardinalitas di Bab 9): satu pelanggan bisa memiliki banyak pesanan, namun satu pesanan hanya dilakukan oleh satu pelanggan. Pola yang sama berlaku antara PRODUK dan PESANAN. Simbol PK di samping sebuah kolom menandakan Primary Key, sementara FK menandakan Foreign Key yang merujuk ke tabel lain, persis seperti yang sudah dibahas di Bab 9.

Satu pelanggan bisa punya banyak pesanan, satu produk bisa muncul di banyak pesanan (1:N)

Struktur Dasar Penulisan JOIN

Struktur umum penulisan JOIN dalam SQL adalah sebagai berikut:

SELECT kolom_yang_diinginkan
FROM tabel_pertama
JOIN tabel_kedua ON tabel_pertama.kolom_penghubung = tabel_kedua.kolom_penghubung;

Klausa ON adalah bagian yang menentukan berdasarkan kolom apa kedua tabel tersebut dihubungkan, biasanya berupa kesamaan antara Primary Key di satu tabel dengan Foreign Key di tabel lainnya. Sebagai ilustrasi paling dasar, untuk menampilkan nama pelanggan beserta nilai transaksi dari tabel pesanan dan pelanggan milik Belanjain, querynya ditulis sebagai:

SELECT pelanggan.nama, pesanan.nilai_transaksi
FROM pesanan
JOIN pelanggan ON pesanan.id_pelanggan = pelanggan.id_pelanggan;

Perhatikan penulisan pelanggan.nama dan pesanan.nilai_transaksi, yaitu penulisan nama kolom yang didahului nama tabelnya, dipisahkan tanda titik. Penulisan seperti ini penting terutama ketika kedua tabel yang digabungkan memiliki nama kolom yang sama, misalnya kedua tabel sama-sama memiliki kolom id_pelanggan, sehingga SQL perlu tahu persis kolom dari tabel mana yang dimaksud.

INNER JOIN: Hanya Data yang Cocok di Kedua Tabel

INNER JOIN hanya menampilkan baris-baris yang memiliki kecocokan di kedua tabel yang digabungkan. Jika sebuah baris di tabel pertama tidak memiliki pasangan yang cocok di tabel kedua, atau sebaliknya, baris tersebut tidak akan ikut ditampilkan sama sekali dalam hasil query.

Sebagai ilustrasi dalam konteks Belanjain, bayangkan tabel pelanggan memiliki 1.000 pelanggan terdaftar, namun hanya 800 di antaranya yang pernah melakukan pemesanan (tercatat di tabel pesanan), sementara 200 pelanggan lainnya baru mendaftar namun belum pernah berbelanja sama sekali. Jika kamu menggunakan INNER JOIN antara tabel pelanggan dan pesanan, hasilnya hanya akan menampilkan 800 pelanggan yang memang memiliki riwayat pesanan, sementara 200 pelanggan yang belum pernah berbelanja tidak akan muncul sama sekali dalam hasil, karena mereka tidak memiliki baris yang cocok di tabel pesanan.

SELECT pelanggan.nama, pesanan.nilai_transaksi, pesanan.tanggal_pesanan
FROM pelanggan
INNER JOIN pesanan ON pelanggan.id_pelanggan = pesanan.id_pelanggan;

LEFT JOIN: Seluruh Data Tabel Kiri, Dilengkapi yang Cocok dari Kanan

LEFT JOIN menampilkan seluruh baris dari tabel pertama (tabel yang disebut di sisi kiri, yaitu setelah FROM), ditambah data yang cocok dari tabel kedua jika memang ada kecocokannya. Jika sebuah baris di tabel kiri tidak memiliki pasangan yang cocok di tabel kanan, baris tersebut tetap ditampilkan, namun kolom-kolom yang seharusnya berasal dari tabel kanan akan berisi nilai NULL (kosong).

Melanjutkan contoh sebelumnya, jika kamu ingin menampilkan seluruh 1.000 pelanggan Belanjain, termasuk 200 pelanggan yang belum pernah berbelanja, kamu menggunakan LEFT JOIN dengan tabel pelanggan di sisi kiri:

SELECT pelanggan.nama, pesanan.nilai_transaksi, pesanan.tanggal_pesanan
FROM pelanggan
LEFT JOIN pesanan ON pelanggan.id_pelanggan = pesanan.id_pelanggan;

Hasil query ini akan menampilkan seluruh 1.000 pelanggan. Untuk 800 pelanggan yang memiliki riwayat pesanan, kolom nilai_transaksi dan tanggal_pesanan akan terisi sesuai data pesanan mereka. Namun untuk 200 pelanggan yang belum pernah berbelanja, kedua kolom tersebut akan menampilkan NULL, karena memang tidak ada data pesanan yang cocok untuk mereka. LEFT JOIN sangat berguna justru ketika kamu ingin mengetahui data yang "hilang" seperti ini, misalnya untuk mengidentifikasi pelanggan yang belum pernah berbelanja sebagai target kampanye pemasaran khusus.

RIGHT JOIN: Seluruh Data Tabel Kanan, Dilengkapi yang Cocok dari Kiri

RIGHT JOIN bekerja dengan logika yang persis berkebalikan dari LEFT JOIN, yaitu menampilkan seluruh baris dari tabel kedua (tabel di sisi kanan), ditambah data yang cocok dari tabel pertama jika ada. Penulisannya adalah:

SELECT pelanggan.nama, pesanan.nilai_transaksi
FROM pelanggan
RIGHT JOIN pesanan ON pelanggan.id_pelanggan = pesanan.id_pelanggan;

Query ini akan menampilkan seluruh baris dari tabel pesanan (tabel di sisi kanan), dilengkapi nama pelanggan yang cocok dari tabel pelanggan. Pada praktiknya, RIGHT JOIN jauh lebih jarang dipakai dibanding LEFT JOIN, karena hasil yang sama sebenarnya bisa dicapai dengan menukar urutan tabel dan menggunakan LEFT JOIN. Sebagai ilustrasi, query RIGHT JOIN di atas menghasilkan output yang identik dengan SELECT pelanggan.nama, pesanan.nilai_transaksi FROM pesanan LEFT JOIN pelanggan ON pesanan.id_pelanggan = pelanggan.id_pelanggan;. Karena kesetaraan ini, banyak Data Analyst yang lebih terbiasa selalu menggunakan LEFT JOIN dan cukup menukar urutan tabelnya sesuai kebutuhan, dibanding harus mengingat dua jenis JOIN yang berbeda arah. Meski demikian, penting tetap memahami RIGHT JOIN karena kamu mungkin menemukannya dalam query yang ditulis orang lain.

FULL OUTER JOIN: Seluruh Data dari Kedua Tabel

FULL OUTER JOIN menggabungkan logika LEFT JOIN dan RIGHT JOIN sekaligus, yaitu menampilkan seluruh baris dari kedua tabel, baik yang memiliki kecocokan maupun yang tidak. Jika sebuah baris di salah satu tabel tidak memiliki pasangan di tabel lainnya, kolom-kolom yang berasal dari tabel yang tidak memiliki pasangan tersebut akan berisi NULL.

SELECT pelanggan.nama, pesanan.nilai_transaksi
FROM pelanggan
FULL OUTER JOIN pesanan ON pelanggan.id_pelanggan = pesanan.id_pelanggan;

Sebagai ilustrasi kapan FULL OUTER JOIN berguna, bayangkan selain 200 pelanggan yang belum pernah berbelanja, ada juga kondisi anomali di mana beberapa baris pada tabel pesanan memiliki id_pelanggan yang sudah tidak terdaftar lagi di tabel pelanggan, misalnya karena akun pelanggan tersebut sudah dihapus dari sistem namun riwayat pesanannya masih tersimpan. FULL OUTER JOIN akan menampilkan kedua kondisi anomali ini sekaligus dalam satu hasil, yaitu pelanggan tanpa pesanan (kolom pesanan NULL) maupun pesanan tanpa pelanggan yang valid (kolom pelanggan NULL), sehingga sangat berguna untuk audit data secara menyeluruh, mengecek konsistensi antar tabel.

Ringkasan Perilaku Keempat Jenis JOIN

Jenis JOIN Hasil
INNER JOIN Hanya baris yang cocok di kedua tabel
LEFT JOIN Seluruh baris tabel kiri, NULL pada kolom kanan jika tidak cocok
RIGHT JOIN Seluruh baris tabel kanan, NULL pada kolom kiri jika tidak cocok
FULL OUTER JOIN Seluruh baris dari kedua tabel, NULL di sisi yang tidak cocok

Menangani NULL dari Hasil LEFT JOIN dan RIGHT JOIN

Nilai NULL yang muncul akibat LEFT JOIN, RIGHT JOIN, maupun FULL OUTER JOIN perlu ditangani dengan hati-hati, karena NULL bukan sekadar "kosong" seperti string kosong atau angka nol, melainkan representasi "tidak ada nilai sama sekali" yang memiliki perilaku khusus dalam SQL. Sebagai ilustrasi, jika kamu mencoba menjumlahkan kolom yang berisi NULL menggunakan SUM, NULL tersebut akan diabaikan secara otomatis dan tidak dihitung sebagai nol, sesuai perilaku yang sudah dibahas pada Bab 11 mengenai fungsi agregasi.

Untuk kebutuhan tampilan yang lebih rapi, terutama pada laporan yang akan dibaca oleh tim non-teknis, NULL sebaiknya diganti dengan nilai yang lebih bermakna, misalnya angka nol untuk kolom numerik atau keterangan seperti "Belum Pernah Berbelanja" untuk kolom teks. SQL menyediakan fungsi yang bisa menggantikan NULL dengan nilai pengganti tertentu, sehingga hasil LEFT JOIN yang tadinya menampilkan NULL bisa diubah menjadi keterangan yang lebih informatif, misalnya:

SELECT 
  pelanggan.nama,
  COALESCE(SUM(pesanan.nilai_transaksi), 0) AS total_belanja
FROM pelanggan
LEFT JOIN pesanan ON pelanggan.id_pelanggan = pesanan.id_pelanggan
GROUP BY pelanggan.nama;

Fungsi COALESCE pada contoh di atas akan mengganti hasil NULL (yang muncul untuk pelanggan tanpa riwayat pesanan) menjadi angka 0, sehingga kolom total_belanja selalu menampilkan angka, bukan kosong yang bisa membingungkan pembaca laporan.

Misi Query Tiga Tabel: Customers, Orders, Products

Untuk mengaplikasikan pemahaman JOIN secara menyeluruh, mari bayangkan skenario yang melibatkan penggabungan tiga tabel sekaligus, bukan hanya dua. Tim manajemen Belanjain ingin melihat laporan lengkap yang menampilkan nama pelanggan, nama produk yang dipesan, beserta nilai transaksinya, untuk seluruh pesanan yang tercatat di sistem.

Karena tabel pesanan terhubung dengan tabel pelanggan melalui id_pelanggan, dan juga terhubung dengan tabel produk melalui id_produk, kamu perlu menggabungkan ketiga tabel ini sekaligus dalam satu query, dengan cara menuliskan JOIN lebih dari satu kali secara berurutan:

SELECT 
  pelanggan.nama AS nama_pelanggan,
  produk.nama_produk,
  pesanan.jumlah,
  pesanan.jumlah * produk.harga AS total_nilai
FROM pesanan
JOIN pelanggan ON pesanan.id_pelanggan = pelanggan.id_pelanggan
JOIN produk ON pesanan.id_produk = produk.id_produk
ORDER BY total_nilai DESC;

Perhatikan bagaimana tabel pesanan dijadikan tabel utama yang di-JOIN dua kali secara berurutan, pertama dengan tabel pelanggan, kemudian dengan tabel produk, masing-masing menggunakan klausa ON yang berbeda sesuai kolom penghubungnya masing-masing. Pola menggabungkan lebih dari dua tabel seperti ini sangat umum dalam pekerjaan nyata seorang Data Analyst, karena laporan bisnis yang bermakna sering kali membutuhkan informasi yang tersebar di banyak tabel berbeda sekaligus, bukan hanya dua tabel saja.

Rangkuman Bab
  • Kategori JOIN: INNER (hanya irisan), LEFT (seluruh tabel kiri), RIGHT (seluruh tabel kanan), FULL OUTER (gabungan seluruh baris).
  • Equi-Join & Klausa ON: Menghubungkan tabel menggunakan kesamaan nilai Primary Key = Foreign Key.
  • Imputasi NULL: Menggunakan `COALESCE(kolom, nilai_pengganti)` agar output angka/teks tidak kosong.
  • Multi-Table JOIN: Menggabungkan 3+ tabel secara berantai (pesanan → pelanggan & pesanan → produk) untuk laporan lengkap.