Perbedaan INNER JOIN, LEFT JOIN, RIGHT JOIN, dan FULL JOIN di SQL
TL;DR
JOIN itu buat gabungin data dari 2+ tabel. INNER JOIN ambil yang match aja, LEFT JOIN ambil semua dari tabel kiri, RIGHT JOIN ambil semua dari tabel kanan, FULL JOIN ambil semuanya. Pilih sesuai kebutuhan analisis kamu.
Apa Itu JOIN dan Kenapa Penting Banget?
Pernah bingung kenapa hasil query kamu returnnya lebih banyak atau lebih sedikit dari yang diharapkan? Atau mungkin pernah dapet error "ambiguous column name" yang bikin frustrasi?
Tenang, kamu ga sendirian kok. JOIN emang salah satu konsep SQL yang paling sering bikin pemula garuk-garuk kepala. Tapi setelah paham logikanya, JOIN justru jadi skill paling powerful yang bakal sering kamu pakai sehari-hari.
Kenapa Sih Harus Pakai JOIN?
Bayangin kamu kerja di startup e-commerce Jakarta. Data karyawan disimpan di satu tabel, data departemen di tabel lain. Kalau mau tau "siapa aja yang kerja di departemen Engineering?", kamu perlu gabungin kedua tabel itu.
Nah, JOIN itu cara SQL buat menggabungkan data dari dua atau lebih tabel berdasarkan kolom yang nyambung (biasanya foreign key).
Contoh situasi yang butuh JOIN:
- Gabungin data order dengan data customer
- Lihat produk beserta nama kategorinya
- Tampilkan karyawan beserta nama departemennya
- Analisis transaksi dengan info user
Intinya, kalau data kamu tersebar di beberapa tabel (yang emang best practice dalam database design), JOIN itu wajib dikuasai.
Dataset yang Kita Pakai
Biar gampang dipahami, kita pakai contoh dataset startup di Jakarta. Ada dua tabel: karyawan dan departemen.
Tabel: karyawan
| id | nama | departemen_id | gaji |
|---|---|---|---|
| 1 | Budi Santoso | 1 | 15000000 |
| 2 | Siti Rahayu | 2 | 12000000 |
| 3 | Andi Wijaya | 1 | 18000000 |
| 4 | Dewi Lestari | 3 | 14000000 |
| 5 | Rudi Hartono | NULL | 10000000 |
Tabel: departemen
| id | nama_dept | lokasi |
|---|---|---|
| 1 | Engineering | Sudirman |
| 2 | Marketing | Kuningan |
| 3 | Finance | Sudirman |
| 4 | HR | Kuningan |
Perhatiin nih:
- Rudi Hartono punya departemen_id NULL (belum di-assign)
- Departemen HR (id=4) ga punya karyawan
Kondisi kayak gini sering banget terjadi di real world. Dan di sinilah pemahaman tentang jenis-jenis JOIN jadi penting.
INNER JOIN: Ambil yang Match Aja
INNER JOIN itu yang paling basic dan paling sering dipakai. Konsepnya simpel: cuma ambil data yang ada di KEDUA tabel.
Visualisasi
Karyawan Departemen
┌─────┐ ┌─────┐
│ │ │ │
│ ███████████████████│
│ │ │ │
└─────┘ └─────┘
↑
Bagian yang di-highlight
= hasil INNER JOIN
Syntax
SELECT kolom1, kolom2, ...
FROM tabel_kiri
INNER JOIN tabel_kanan ON tabel_kiri.kolom = tabel_kanan.kolom;
Contoh 1: Basic INNER JOIN
SELECT
k.nama,
k.gaji,
d.nama_dept,
d.lokasi
FROM karyawan k
INNER JOIN departemen d ON k.departemen_id = d.id;
Output:
| nama | gaji | nama_dept | lokasi |
|---|---|---|---|
| Budi Santoso | 15000000 | Engineering | Sudirman |
| Siti Rahayu | 12000000 | Marketing | Kuningan |
| Andi Wijaya | 18000000 | Engineering | Sudirman |
| Dewi Lestari | 14000000 | Finance | Sudirman |
Perhatiin:
- Rudi Hartono ga muncul (karena departemen_id NULL, ga match sama departemen manapun)
- Departemen HR ga muncul (karena ga ada karyawan yang punya departemen_id = 4)
Contoh 2: INNER JOIN dengan Filter
SELECT
k.nama,
k.gaji,
d.nama_dept
FROM karyawan k
INNER JOIN departemen d ON k.departemen_id = d.id
WHERE d.lokasi = 'Sudirman';
Output:
| nama | gaji | nama_dept |
|---|---|---|
| Budi Santoso | 15000000 | Engineering |
| Andi Wijaya | 18000000 | Engineering |
| Dewi Lestari | 14000000 | Finance |
Contoh 3: INNER JOIN dengan Aggregation
SELECT
d.nama_dept,
COUNT(k.id) AS jumlah_karyawan,
AVG(k.gaji) AS rata_rata_gaji
FROM karyawan k
INNER JOIN departemen d ON k.departemen_id = d.id
GROUP BY d.nama_dept;
Output:
| nama_dept | jumlah_karyawan | rata_rata_gaji |
|---|---|---|
| Engineering | 2 | 16500000 |
| Marketing | 1 | 12000000 |
| Finance | 1 | 14000000 |
Kapan Pakai INNER JOIN?
- Kamu cuma butuh data yang punya relasi lengkap di kedua tabel
- Ga peduli sama data yang "yatim" (orphan records)
- Contoh: Laporan penjualan yang cuma nampilin order yang udah ada customer-nya
LEFT JOIN: Ambil Semua dari Tabel Kiri
LEFT JOIN (atau LEFT OUTER JOIN) itu ambil semua data dari tabel kiri, plus data yang match dari tabel kanan. Kalau ga match, kolom dari tabel kanan jadi NULL.
Visualisasi
Karyawan Departemen
┌─────┐ ┌─────┐
│█████│ │ │
│█████████████████████│
│█████│ │ │
└─────┘ └─────┘
↑
Semua dari kiri + yang match dari kanan
Syntax
SELECT kolom1, kolom2, ...
FROM tabel_kiri
LEFT JOIN tabel_kanan ON tabel_kiri.kolom = tabel_kanan.kolom;
Contoh 1: Basic LEFT JOIN
SELECT
k.nama,
k.gaji,
d.nama_dept,
d.lokasi
FROM karyawan k
LEFT JOIN departemen d ON k.departemen_id = d.id;
Output:
| nama | gaji | nama_dept | lokasi |
|---|---|---|---|
| Budi Santoso | 15000000 | Engineering | Sudirman |
| Siti Rahayu | 12000000 | Marketing | Kuningan |
| Andi Wijaya | 18000000 | Engineering | Sudirman |
| Dewi Lestari | 14000000 | Finance | Sudirman |
| Rudi Hartono | 10000000 | NULL | NULL |
Nah, sekarang Rudi Hartono muncul! Tapi nama_dept dan lokasi-nya NULL karena dia ga punya departemen.
Contoh 2: Cari Data yang Ga Match
Ini pattern yang sering banget dipakai: cari data di tabel kiri yang GA ADA di tabel kanan.
SELECT
k.nama,
k.gaji
FROM karyawan k
LEFT JOIN departemen d ON k.departemen_id = d.id
WHERE d.id IS NULL;
Output:
| nama | gaji |
|---|---|
| Rudi Hartono | 10000000 |
Pattern ini berguna banget buat:
- Cari customer yang belum pernah order
- Cari produk yang belum pernah terjual
- Cari user yang belum lengkapin profile
Contoh 3: LEFT JOIN dengan COALESCE
COALESCE itu buat handle NULL values. Berguna banget biar output lebih readable.
SELECT
k.nama,
k.gaji,
COALESCE(d.nama_dept, 'Belum Ditentukan') AS departemen
FROM karyawan k
LEFT JOIN departemen d ON k.departemen_id = d.id;
Output:
| nama | gaji | departemen |
|---|---|---|
| Budi Santoso | 15000000 | Engineering |
| Siti Rahayu | 12000000 | Marketing |
| Andi Wijaya | 18000000 | Engineering |
| Dewi Lestari | 14000000 | Finance |
| Rudi Hartono | 10000000 | Belum Ditentukan |
Kapan Pakai LEFT JOIN?
- Mau keep semua data dari tabel utama
- Perlu analisis data yang "yatim" (ga punya relasi)
- Contoh: List semua karyawan, termasuk yang belum di-assign ke departemen
RIGHT JOIN: Ambil Semua dari Tabel Kanan
RIGHT JOIN kebalikannya LEFT JOIN. Ambil semua data dari tabel kanan, plus data yang match dari tabel kiri.
Visualisasi
Karyawan Departemen
┌─────┐ ┌─────┐
│ │ │█████│
│█████████████████████│
│ │ │█████│
└─────┘ └─────┘
↑
Yang match dari kiri + semua dari kanan
Syntax
SELECT kolom1, kolom2, ...
FROM tabel_kiri
RIGHT JOIN tabel_kanan ON tabel_kiri.kolom = tabel_kanan.kolom;
Contoh 1: Basic RIGHT JOIN
SELECT
k.nama,
k.gaji,
d.nama_dept,
d.lokasi
FROM karyawan k
RIGHT JOIN departemen d ON k.departemen_id = d.id;
Output:
| nama | gaji | nama_dept | lokasi |
|---|---|---|---|
| Budi Santoso | 15000000 | Engineering | Sudirman |
| Andi Wijaya | 18000000 | Engineering | Sudirman |
| Siti Rahayu | 12000000 | Marketing | Kuningan |
| Dewi Lestari | 14000000 | Finance | Sudirman |
| NULL | NULL | HR | Kuningan |
Sekarang departemen HR muncul! Tapi nama dan gaji-nya NULL karena ga ada karyawan di departemen itu.
Contoh 2: Cari Departemen yang Kosong
SELECT
d.nama_dept,
d.lokasi
FROM karyawan k
RIGHT JOIN departemen d ON k.departemen_id = d.id
WHERE k.id IS NULL;
Output:
| nama_dept | lokasi |
|---|---|
| HR | Kuningan |
Tips: RIGHT JOIN Jarang Dipakai
Jujur aja nih, dalam prakteknya RIGHT JOIN jarang dipakai. Kenapa? Karena kamu bisa dapetin hasil yang sama dengan nuker posisi tabel dan pakai LEFT JOIN.
Query ini:
SELECT * FROM karyawan k RIGHT JOIN departemen d ON k.departemen_id = d.id;
Sama hasilnya dengan:
SELECT * FROM departemen d LEFT JOIN karyawan k ON d.id = k.departemen_id;
Kebanyakan developer lebih prefer LEFT JOIN karena lebih gampang dibaca (tabel utama selalu di kiri).
FULL OUTER JOIN: Ambil Semuanya
FULL OUTER JOIN (atau FULL JOIN) itu gabungan LEFT dan RIGHT JOIN. Ambil semua data dari kedua tabel, match yang bisa di-match, sisanya dikasih NULL.
Visualisasi
Karyawan Departemen
┌─────┐ ┌─────┐
│█████│ │█████│
│█████████████████████│
│█████│ │█████│
└─────┘ └─────┘
↑ ↑
Semua dari kedua tabel
Syntax
SELECT kolom1, kolom2, ...
FROM tabel_kiri
FULL OUTER JOIN tabel_kanan ON tabel_kiri.kolom = tabel_kanan.kolom;
Contoh: FULL OUTER JOIN
SELECT
k.nama,
k.gaji,
d.nama_dept,
d.lokasi
FROM karyawan k
FULL OUTER JOIN departemen d ON k.departemen_id = d.id;
Output:
| nama | gaji | nama_dept | lokasi |
|---|---|---|---|
| Budi Santoso | 15000000 | Engineering | Sudirman |
| Andi Wijaya | 18000000 | Engineering | Sudirman |
| Siti Rahayu | 12000000 | Marketing | Kuningan |
| Dewi Lestari | 14000000 | Finance | Sudirman |
| Rudi Hartono | 10000000 | NULL | NULL |
| NULL | NULL | HR | Kuningan |
Sekarang SEMUA data muncul:
- Karyawan yang punya departemen
- Karyawan yang ga punya departemen (Rudi)
- Departemen yang ga punya karyawan (HR)
Catatan: MySQL ga support FULL OUTER JOIN secara native. Kamu perlu pakai UNION:
SELECT k.nama, k.gaji, d.nama_dept, d.lokasi
FROM karyawan k
LEFT JOIN departemen d ON k.departemen_id = d.id
UNION
SELECT k.nama, k.gaji, d.nama_dept, d.lokasi
FROM karyawan k
RIGHT JOIN departemen d ON k.departemen_id = d.id;
Kapan Pakai FULL OUTER JOIN?
- Mau analisis data yang "yatim" di kedua sisi
- Data reconciliation atau audit
- Contoh: Bandingkan data dari dua sistem yang harusnya sama
CROSS JOIN: Semua Kombinasi
CROSS JOIN itu beda sendiri. Dia bikin semua kemungkinan kombinasi dari kedua tabel (Cartesian Product).
Syntax
SELECT kolom1, kolom2, ...
FROM tabel_kiri
CROSS JOIN tabel_kanan;
Contoh: CROSS JOIN
SELECT
k.nama,
d.nama_dept
FROM karyawan k
CROSS JOIN departemen d;
Kalau karyawan ada 5 baris dan departemen ada 4 baris, hasilnya 5 x 4 = 20 baris.
Kapan Pakai CROSS JOIN?
- Bikin kombinasi untuk analisis (misal: semua kombinasi produk dan region)
- Generate test data
- Bikin date dimension table
Hati-hati: CROSS JOIN bisa bikin hasil yang BESAR BANGET. Kalau dua tabel masing-masing 10.000 baris, hasilnya 100 juta baris!
Cheat Sheet Perbandingan JOIN
| Jenis JOIN | Hasil | Use Case |
|---|---|---|
| INNER JOIN | Hanya data yang match di kedua tabel | Report standar, data yang valid aja |
| LEFT JOIN | Semua dari kiri + match dari kanan | Keep semua data utama, cek missing relations |
| RIGHT JOIN | Semua dari kanan + match dari kiri | Jarang dipakai, bisa diganti LEFT JOIN |
| FULL OUTER JOIN | Semua dari kedua tabel | Data reconciliation, audit |
| CROSS JOIN | Semua kombinasi | Generate combinations, test data |
Kesalahan Umum Saat Pakai JOIN
Kesalahan #1: Lupa ON Clause
-- SALAH: Ini jadi CROSS JOIN!
SELECT * FROM karyawan k JOIN departemen d;
-- BENAR
SELECT * FROM karyawan k JOIN departemen d ON k.departemen_id = d.id;
Kesalahan #2: Ambiguous Column Name
-- SALAH: Kolom 'id' ada di kedua tabel
SELECT id, nama FROM karyawan k JOIN departemen d ON k.departemen_id = d.id;
-- BENAR: Pakai alias
SELECT k.id, k.nama FROM karyawan k JOIN departemen d ON k.departemen_id = d.id;
Kesalahan #3: Salah Pilih Jenis JOIN
Ini yang paling sering terjadi. Pastiin kamu paham data mana yang mau di-keep:
- Mau keep semua karyawan? Pakai LEFT JOIN
- Mau keep semua departemen? Pakai RIGHT JOIN (atau swap dan pakai LEFT)
- Cuma mau yang match? Pakai INNER JOIN
Kesalahan #4: JOIN Tanpa Index
Kalau query JOIN kamu lambat, kemungkinan besar kolom yang dipakai buat JOIN ga ada index-nya. Pastikan kolom foreign key udah di-index.
CREATE INDEX idx_karyawan_dept ON karyawan(departemen_id);
Latihan
Coba kerjain soal ini pakai dataset karyawan dan departemen di atas:
Soal 1: Tampilkan nama karyawan dan nama departemen untuk semua karyawan di lokasi 'Sudirman'
Klik untuk lihat solusi
SELECT k.nama, d.nama_dept
FROM karyawan k
INNER JOIN departemen d ON k.departemen_id = d.id
WHERE d.lokasi = 'Sudirman';
Soal 2: Hitung jumlah karyawan per departemen, termasuk departemen yang kosong
Klik untuk lihat solusi
SELECT
d.nama_dept,
COUNT(k.id) AS jumlah_karyawan
FROM departemen d
LEFT JOIN karyawan k ON d.id = k.departemen_id
GROUP BY d.nama_dept;
Soal 3: Cari karyawan yang belum di-assign ke departemen manapun
Klik untuk lihat solusi
SELECT k.nama, k.gaji
FROM karyawan k
LEFT JOIN departemen d ON k.departemen_id = d.id
WHERE d.id IS NULL;
Key Takeaways
-
INNER JOIN ambil data yang match di kedua tabel. Paling sering dipakai untuk report standar.
-
LEFT JOIN keep semua data dari tabel kiri. Berguna buat cek data yang ga punya relasi.
-
RIGHT JOIN kebalikan LEFT JOIN, tapi jarang dipakai. Lebih baik swap tabel dan pakai LEFT JOIN.
-
FULL OUTER JOIN ambil semua data dari kedua tabel. Cocok buat data reconciliation.
-
Selalu pakai alias buat menghindari ambiguous column error dan bikin query lebih readable.
Selanjutnya
Kalau kamu udah paham JOIN, next step-nya:
- Cara Menggunakan Subquery di SQL
- Common Table Expression (CTE) di SQL
- Window Functions SQL
Mulai Latihan Sekarang!
Teori doang ga cukup. Langsung praktek di NgulikSQL buat latihan soal JOIN dengan dataset Indonesia. Atau kalau mau explore lebih, coba SQLBolt dan DataLemur.
Ada pertanyaan? Comment di bawah atau DM aku di Instagram @ngulikdata.
Happy querying!
Artikel Terkait
Fungsi GROUP BY dan HAVING di SQL: Panduan Lengkap
Pelajari cara pakai GROUP BY dan HAVING untuk agregasi data di SQL dengan contoh dataset penjualan UMKM Indonesia
Cara Menggunakan Subquery di SQL (Dengan 10 Contoh Praktis)
Pelajari cara pakai subquery di SQL dengan 10 contoh praktis menggunakan dataset e-commerce Indonesia
Cara Install PostgreSQL di Windows, Mac, dan Linux (Panduan Lengkap)
Panduan step-by-step install PostgreSQL di Windows, macOS, dan Linux lengkap dengan pgAdmin dan troubleshooting