6 JOIN SQL yang Perlu Anda Ketahui (Dengan Contoh Sederhana dan Jelas)
Pelajari 6 JOIN SQL yang harus dikuasai—INNER, LEFT, RIGHT, FULL OUTER, CROSS, dan SELF—dengan contoh praktis dan jebakan umum.

Apa itu SQL JOIN dan Mengapa Anda Menggunakannya
Sebuah SQL JOIN memungkinkan Anda menggabungkan baris dari dua (atau lebih) tabel menjadi satu hasil dengan mencocokkannya pada kolom terkait—biasanya sebuah ID.
Mengapa JOIN penting
Kebanyakan basis data nyata sengaja dibagi ke tabel yang terpisah agar Anda tidak mengulang informasi yang sama berulang kali. Misalnya, nama pelanggan berada di tabel customers, sedangkan pembeliannya berada di tabel orders. JOIN adalah cara Anda menghubungkan kembali potongan-potongan itu saat Anda butuh jawaban.
Itulah alasan JOIN muncul di mana-mana dalam pelaporan dan analisis:
- Membangun laporan penjualan yang menyertakan nama pelanggan, total pesanan, dan status pembayaran
- Menemukan pelanggan yang belum pernah memesan
- Mengaudit ketidakcocokan, seperti pesanan tanpa pembayaran
- Membuat ringkasan “satu baris per pelanggan” dari banyak baris terkait
Tanpa JOIN, Anda akan terjebak menjalankan query terpisah dan menggabungkan hasil secara manual—lambat, rawan kesalahan, dan sulit diulang.
Jika Anda membangun produk di atas basis data relasional (dashboard, panel admin, alat internal, portal pelanggan), JOIN juga yang mengubah “tabel mentah” menjadi tampilan yang dapat digunakan pengguna. Platform seperti Koder.ai (yang menghasilkan aplikasi React + Go + PostgreSQL dari chat) tetap bergantung pada dasar JOIN ketika Anda butuh halaman daftar, laporan, dan layar rekonsiliasi yang akurat—karena logika basis data tidak hilang walau pengembangan jadi lebih cepat.
6 tipe JOIN yang paling sering dipakai
Panduan ini fokus pada enam JOIN yang mencakup sebagian besar pekerjaan SQL sehari-hari:
- INNER JOIN: mengembalikan hanya baris yang cocok di kedua tabel (bagus untuk “tampilkan hubungan yang terkonfirmasi”).
- LEFT JOIN: mempertahankan setiap baris dari tabel kiri, dan mencocokkan apa yang bisa dari kanan (bagus untuk “sertakan data terkait yang hilang”).
- RIGHT JOIN: cerminan LEFT JOIN (lebih jarang, tapi berguna tergantung gaya query atau keterbacaan).
- FULL OUTER JOIN: mempertahankan semua baris dari kedua tabel, dicocokkan bila memungkinkan (bagus untuk rekonsiliasi dan menemukan celah).
- CROSS JOIN: menghasilkan semua kombinasi baris (berguna untuk membuat kalender, skenario, atau data uji—juga mudah disalahgunakan).
- SELF JOIN: menggabungkan tabel dengan dirinya sendiri (berguna untuk hirarki seperti karyawan/manager).
Catatan singkat tentang sintaks
Sintaks JOIN sangat mirip di sebagian besar DBMS (PostgreSQL, MySQL, SQL Server, SQLite). Ada beberapa perbedaan—terutama dukungan FULL OUTER JOIN dan beberapa perilaku edge-case—tetapi konsep dan pola inti mudah dipindahkan antar platform.
Tabel Contoh yang Kita Pakai (Customers, Orders, Payments)
Untuk menyederhanakan contoh JOIN, kita akan menggunakan tiga tabel kecil yang mencerminkan setup dunia nyata: customers membuat orders, dan orders mungkin (atau mungkin tidak) memiliki payments.
Catatan kecil sebelum mulai: tabel contoh di bawah hanya menunjukkan beberapa kolom, tetapi beberapa query nanti merujuk kolom tambahan (seperti order_date, created_at, status, atau paid_at) untuk menunjukkan pola umum. Anggap kolom-kolom itu sebagai field “tipikal” yang sering ada di skema produksi.
1) customers
Primary key: customer_id
| customer_id | name |
|---|---|
| 1 | Ava |
| 2 | Ben |
| 3 | Chen |
| 4 | Dia |
2) orders
Primary key: order_id
Foreign key: customer_id → customers.customer_id
| order_id | customer_id | order_total |
|---|---|---|
| 101 | 1 | 50 |
| 102 | 1 | 120 |
| 103 | 2 | 35 |
| 104 | 5 | 70 |
Perhatikan order_id = 104 merujuk customer_id = 5, yang tidak ada di customers. Kasus “kecocokan hilang” ini berguna untuk melihat bagaimana LEFT JOIN, RIGHT JOIN, dan FULL OUTER JOIN berperilaku.
3) payments
Primary key: payment_id
Foreign key: order_id → orders.order_id
| payment_id | order_id | amount |
|---|---|---|
| 9001 | 101 | 50 |
| 9002 | 102 | 60 |
| 9003 | 102 | 60 |
| 9004 | 999 | 25 |
Dua detail pengajaran penting di sini:
order_id = 102punya dua baris pembayaran (pembayaran terpisah). Saat Anda joinorderskepayments, order itu akan muncul dua kali—di sinilah duplikasi sering mengejutkan orang.payment_id = 9004merujukorder_id = 999, yang tidak ada diorders. Itu menciptakan kasus “tidak cocok” lain.
Apa yang diharapkan ketika kita JOIN tabel ini
- Baris yang cocok: mis. customer 1 ↔ orders 101/102; order 101 ↔ payment 9001.
- Baris yang tidak cocok: mis. customer 3 dan 4 tidak punya orders; order 104 tidak punya customer; payment 9004 tidak punya order.
- Duplikasi: menggabungkan
orderskepaymentsakan mengulangi order 102 karena punya dua pembayaran.
INNER JOIN: Hanya Simpan Baris yang Cocok
INNER JOIN mengembalikan hanya baris di mana ada kecocokan di kedua tabel. Jika seorang pelanggan tidak punya pesanan, mereka tidak akan muncul di hasil. Jika sebuah order merujuk ke customer yang tidak ada (data buruk), order itu juga tidak akan muncul.
Pola dasar
Anda memilih tabel “kiri”, join tabel “kanan”, dan menghubungkannya dengan kondisi di klausa ON.
SELECT
c.customer_id,
c.name,
o.order_id,
o.order_date
FROM customers c
INNER JOIN orders o
ON o.customer_id = c.customer_id;
Ide kuncinya adalah baris ON o.customer_id = c.customer_id: itu memberi tahu SQL bagaimana baris berkaitan.
Kasus penggunaan nyata: pelanggan yang membuat pesanan
Jika Anda ingin daftar hanya pelanggan yang benar-benar membuat setidaknya satu pesanan (dan detail pesanan), INNER JOIN adalah pilihan natural:
SELECT
c.name,
o.order_id,
o.total_amount
FROM customers c
INNER JOIN orders o
ON o.customer_id = c.customer_id
ORDER BY o.order_id;
Ini berguna untuk hal-hal seperti “kirim email tindak lanjut pesanan” atau “hitung pendapatan per pelanggan” (ketika Anda hanya peduli tentang pelanggan yang bertransaksi).
Kesalahan umum: kondisi join hilang atau tidak jelas
Jika Anda menulis join tapi lupa kondisi ON (atau join pada kolom yang salah), Anda bisa tak sengaja membuat produk Kartesius (setiap customer digabungkan dengan setiap order) atau menghasilkan kecocokan yang salah secara halus.
Buruk (jangan lakukan ini):
SELECT c.name, o.order_id
FROM customers c
JOIN orders o;
Selalu pastikan Anda punya kondisi join yang jelas di ON (atau USING pada kasus tertentu—akan dibahas nanti).
LEFT JOIN: Pertahankan Semua dari Tabel Kiri
LEFT JOIN mengembalikan semua baris dari tabel kiri, dan menambahkan data yang cocok dari tabel kanan jika ada. Jika tidak ada kecocokan, kolom sisi kanan akan bernilai NULL.
Kapan dipakai
Gunakan LEFT JOIN ketika Anda menginginkan daftar lengkap dari tabel utama Anda, plus data terkait opsional.
Contoh: “Tunjukkan semua pelanggan, dan sertakan pesanan mereka jika ada.”
SELECT
c.customer_id,
c.name,
o.order_id,
o.order_date
FROM customers c
LEFT JOIN orders o
ON o.customer_id = c.customer_id
ORDER BY c.customer_id;
- Pelanggan dengan pesanan akan muncul dengan detail pesanan.
- Pelanggan tanpa pesanan tetap muncul, tetapi
o.order_id(dan kolomorderslainnya) akanNULL.
Menemukan baris “tanpa kecocokan” (pola klasik)
Alasan sangat umum menggunakan LEFT JOIN adalah untuk menemukan item yang tidak memiliki record terkait.
Contoh: “Pelanggan mana yang tidak pernah membuat pesanan?”
SELECT
c.customer_id,
c.name
FROM customers c
LEFT JOIN orders o
ON o.customer_id = c.customer_id
WHERE o.order_id IS NULL;
Kondisi WHERE ... IS NULL itu menjaga hanya baris tabel kiri yang tidak ditemukan kecocokan.
Waspada: kecocokan ganda menggandakan baris
LEFT JOIN bisa “menduplikasi” baris tabel kiri ketika ada banyak baris cocok di kanan.
Jika satu pelanggan punya 3 pesanan, pelanggan itu akan muncul 3 kali—satu kali per pesanan. Itu ekspektasi yang benar, tapi bisa mengejutkan jika Anda sedang mencoba menghitung pelanggan.
Contoh, ini menghitung pesanan (bukan pelanggan):
SELECT COUNT(*)
FROM customers c
LEFT JOIN orders o
ON o.customer_id = c.customer_id;
Jika tujuan Anda menghitung pelanggan, biasanya Anda menghitung kunci pelanggan instead (sering dengan COUNT(DISTINCT c.customer_id)), tergantung apa yang ingin Anda ukur.
RIGHT JOIN: Pertahankan Semua dari Tabel Kanan
RIGHT JOIN mempertahankan semua baris dari tabel kanan, dan hanya baris yang cocok dari tabel kiri. Jika tidak ada kecocokan, kolom tabel kiri akan NULL. Ini pada dasarnya adalah cermin dari LEFT JOIN.
Contoh sederhana
Dengan tabel contoh kita, bayangkan Anda ingin menampilkan semua pembayaran, bahkan jika tidak dapat dihubungkan ke order (mungkin order dihapus, atau data pembayaran berantakan).
SELECT
o.order_id,
o.customer_id,
p.payment_id,
p.amount,
p.paid_at
FROM orders o
RIGHT JOIN payments p
ON o.order_id = p.order_id;
Yang Anda dapatkan:
- Semua pembayaran disertakan (karena
paymentsada di kanan). - Jika pembayaran tidak punya order yang cocok, maka
o.order_iddano.customer_idakanNULL.
Query yang sama sebagai LEFT JOIN (sering lebih disukai)
Sebagian besar waktu, Anda bisa menulis ulang RIGHT JOIN sebagai LEFT JOIN dengan menukar urutan tabel:
SELECT
o.order_id,
o.customer_id,
p.payment_id,
p.amount,
p.paid_at
FROM payments p
LEFT JOIN orders o
ON o.order_id = p.order_id;
Ini mengembalikan hasil yang sama, tetapi banyak orang merasa lebih mudah dibaca: Anda mulai dengan tabel “utama” yang Anda pedulikan (di sini, payments) lalu “opsional” menarik data terkait.
Keterbacaan: kenapa banyak tim menghindari RIGHT JOIN
Banyak pedoman gaya SQL menyarankan menghindari RIGHT JOIN karena memaksa pembaca membalik pola yang umum:
- “Mulai dengan tabel utama”
- “LEFT JOIN tabel tambahan”
Ketika relasi opsional selalu ditulis sebagai LEFT JOIN, query jadi lebih mudah di-scan.
Kapan RIGHT JOIN masih berguna
RIGHT JOIN bisa berguna saat Anda mengedit query yang sudah ada dan menyadari tabel “harus-dipertahankan” sedang berada di kanan. Daripada menulis ulang seluruh query (terutama jika panjang dengan banyak join), mengganti satu join menjadi RIGHT JOIN bisa jadi perubahan cepat dan berisiko rendah.
FULL OUTER JOIN: Pertahankan Semua Baris dari Kedua Tabel
FULL OUTER JOIN mengembalikan semua baris dari kedua tabel.
- Jika baris cocok pada key join, Anda mendapat satu baris gabungan (seperti
INNER JOIN). - Jika baris hanya ada di tabel kiri, ia tetap muncul — dengan
NULLpada kolom tabel kanan. - Jika baris hanya ada di tabel kanan, ia tetap muncul — dengan
NULLpada kolom tabel kiri.
Kapan berguna
Kasus bisnis klasik adalah merekonciliikan orders vs. payments:
- Anda ingin melihat order yang dibayar (kecocokan)
- order yang belum dibayar (order ada, payment hilang)
- pembayaran tersisa (payment ada, tetapi tidak ada order — error data, refund, referensi salah, dll.)
Contoh:
SELECT
o.order_id,
o.customer_id,
p.payment_id,
p.amount
FROM orders o
FULL OUTER JOIN payments p
ON p.order_id = o.order_id;
Dukungan database (siapa yang bisa menjalankannya langsung)
FULL OUTER JOIN didukung di PostgreSQL, SQL Server, dan Oracle.
Tidak tersedia di MySQL dan SQLite (Anda perlu solusi pengganti).
Alternatif portabel: gabungkan LEFT JOIN dan RIGHT JOIN dengan UNION
Jika database Anda tidak mendukung FULL OUTER JOIN, Anda bisa mensimulasikannya dengan menggabungkan:
- semua baris dari
orders(dengan pembayaran yang cocok bila tersedia), dan - semua baris dari
paymentsyang tidak cocok ke order.
Satu pola umum:
SELECT o.order_id, o.customer_id, p.payment_id, p.amount
FROM orders o
LEFT JOIN payments p
ON p.order_id = o.order_id
UNION
SELECT o.order_id, o.customer_id, p.payment_id, p.amount
FROM orders o
RIGHT JOIN payments p
ON p.order_id = o.order_id;
Tip: ketika Anda melihat NULL di satu sisi, itu sinyal bahwa baris itu “hilang” di tabel lain — persis yang Anda inginkan untuk audit dan rekonsiliasi.
CROSS JOIN: Buat Semua Kombinasi (Gunakan dengan Hati-hati)
CROSS JOIN mengembalikan setiap pasangan kemungkinan baris dari dua tabel. Jika tabel A punya 3 baris dan tabel B punya 4 baris, hasilnya akan punya 3 × 4 = 12 baris. Ini juga disebut produk Kartesius.
Kedengarannya menakutkan—dan memang bisa—tetapi berguna ketika Anda ingin menghasilkan kombinasi.
Contoh kecil dan aman: ukuran × warna (membuat SKU)
Bayangkan Anda menyimpan opsi produk di tabel terpisah:
sizes: S, M, Lcolors: Red, Blue
CROSS JOIN dapat menghasilkan semua varian (berguna untuk membuat SKU, pra-membangun katalog, atau pengujian):
SELECT
s.size,
c.color
FROM sizes AS s
CROSS JOIN colors AS c;
Hasil (3 × 2 = 6 baris):
- S / Red
- S / Blue
- M / Red
- M / Blue
- L / Red
- L / Blue
Peringatan besar: hasil tumbuh cepat
Karena jumlah baris berlipat, CROSS JOIN bisa meledak dengan cepat:
- 10.000 customers × 50 products = 500.000 baris
- 100.000 × 100.000 = 10.000.000.000 baris
Itu bisa memperlambat query, membebani memori, dan menghasilkan keluaran yang tidak berguna. Jika Anda butuh kombinasi, jaga input kecil dan pertimbangkan menambahkan limit atau filter secara terkendali.
SELF JOIN: Gabungkan Tabel dengan Dirinya Sendiri
SELF JOIN adalah persis apa yang namanya: Anda menggabungkan tabel dengan dirinya sendiri. Ini berguna ketika satu baris dalam tabel berhubungan dengan baris lain di tabel yang sama—paling umum untuk hubungan parent/child seperti karyawan dan manager.
Kenapa perlu alias (dan bagaimana mereka membantu)
Karena Anda menggunakan tabel yang sama dua kali, Anda harus memberi setiap “salinan” alias berbeda. Alias membuat query terbaca dan memberi tahu SQL sisi mana yang dimaksud.
Pola umum:
euntuk employeemuntuk manager
Contoh praktis: karyawan dan namanya manager
Bayangkan tabel employees seperti ini:
idnamemanager_id(mengarah keidemployee lain)
Untuk menampilkan setiap karyawan dengan nama managernya:
SELECT
e.id,
e.name AS employee_name,
m.name AS manager_name
FROM employees e
LEFT JOIN employees m
ON e.manager_id = m.id;
Menangani top-level employees (manager_id NULL)
Perhatikan query menggunakan LEFT JOIN, bukan INNER JOIN. Itu penting karena beberapa karyawan mungkin tidak punya manager (mis. CEO). Dalam kasus itu, manager_id sering NULL, dan LEFT JOIN mempertahankan baris karyawan sambil menampilkan manager_name sebagai NULL.
Jika Anda menggunakan INNER JOIN, karyawan level atas itu akan hilang dari hasil karena tidak ada baris manager untuk di-join.
Kondisi Join: ON vs USING (dan Mengapa Penting)
JOIN tidak “tahu” sendiri bagaimana dua tabel berhubungan—Anda harus memberitahukannya. Hubungan itu didefinisikan dalam kondisi join, dan sebaiknya berada tepat di sebelah klausa JOIN karena menjelaskan bagaimana tabel cocok, bukan bagaimana Anda ingin memfilter hasil akhir.
ON: paling fleksibel (dan paling umum)
Gunakan ON saat Anda ingin kontrol penuh atas logika pencocokan—nama kolom berbeda, kondisi ganda, atau aturan tambahan.
SELECT
c.customer_id,
c.name,
o.order_id,
o.created_at
FROM customers AS c
INNER JOIN orders AS o
ON o.customer_id = c.customer_id;
ON juga tempat Anda dapat mendefinisikan pencocokan yang lebih kompleks (mis. mencocokkan dua kolom) tanpa membuat query menjadi tebakan-tebakan.
USING: lebih singkat, tapi hanya untuk kolom bernama sama
Beberapa DB (seperti PostgreSQL dan MySQL) mendukung USING. Ini adalah shorthand yang nyaman ketika kedua tabel punya kolom dengan nama yang sama dan Anda ingin join pada kolom itu.
SELECT
customer_id,
name,
order_id
FROM customers
JOIN orders
USING (customer_id);
Salah satu keuntungan: USING biasanya mengembalikan hanya satu kolom customer_id di output (alih-alih dua salinan).
Hindari nama kolom ambigu: selalu kualifikasi saat join
Setelah Anda join tabel, nama kolom sering tumpang tindih (id, created_at, status). Jika Anda menulis SELECT id, database mungkin memberi error “ambiguous column”—atau lebih buruk, Anda bisa membaca id yang salah.
Utamakan prefix tabel (atau alias) untuk kejelasan:
SELECT c.customer_id, o.order_id
FROM customers AS c
JOIN orders AS o
ON o.customer_id = c.customer_id;
Lewati SELECT * dalam query yang di-join
SELECT * cepat menjadi berantakan dengan join: Anda menarik kolom yang tidak diperlukan, risiko nama ganda, dan menyulitkan pemahaman tujuan query. Sebagai gantinya, pilih kolom yang benar-benar Anda butuhkan. Hasil lebih bersih, lebih mudah dipelihara, dan sering lebih efisien—terutama saat tabel lebar.
Memfilter Data Join: WHERE vs ON
Saat Anda join tabel, WHERE dan ON keduanya “memfilter”, tetapi mereka melakukannya pada momen berbeda.
- ON memutuskan baris mana yang cocok selama join.
- WHERE memfilter hasil akhir setelah join terbentuk.
Perbedaan timing itu penyebab orang sering tidak sengaja mengubah LEFT JOIN menjadi INNER JOIN.
Bagaimana WHERE bisa tanpa sengaja merusak LEFT JOIN
Katakan Anda ingin semua pelanggan, bahkan yang tidak punya pesanan berbayar terbaru.
SELECT c.customer_id, c.name, o.order_id, o.status, o.order_date
FROM customers c
LEFT JOIN orders o
ON o.customer_id = c.customer_id
WHERE o.status = 'PAID'
AND o.order_date >= DATE '2025-01-01';
Masalah: untuk pelanggan tanpa order yang cocok, o.status dan o.order_date adalah NULL. Klausa WHERE menolak baris-barus itu, sehingga pelanggan tanpa kecocokan hilang—LEFT JOIN Anda berperilaku seperti INNER JOIN.
Pindahkan kondisi terkait join ke ON untuk mempertahankan baris yang tak cocok
SELECT c.customer_id, c.name, o.order_id, o.status, o.order_date
FROM customers c
LEFT JOIN orders o
ON o.customer_id = c.customer_id
AND o.status = 'PAID'
AND o.order_date >= DATE '2025-01-01';
Sekarang pelanggan tanpa pesanan yang memenuhi syarat tetap muncul (dengan kolom order NULL), yang biasanya tujuan LEFT JOIN.
Daftar cepat: apa yang masuk ON vs WHERE?
- Taruh kondisi di ON ketika mereka menjelaskan baris tabel kanan mana yang boleh cocok (filter status/date/tipe pada tabel yang di-join).
- Taruh kondisi di WHERE ketika mereka menjelaskan baris final mana yang ingin Anda pertahankan (filter pada tabel kiri, atau ketika Anda memang ingin memaksa kecocokan).
- Jika Anda perlu “pertahankan semua baris kiri, tapi batasi baris kanan”, pilih LEFT JOIN + kondisi di ON.
- Jika benar-benar bermaksud “hanya baris yang cocok”, gunakan INNER JOIN (atau
WHERE o.order_id IS NOT NULLsecara eksplisit).
Menghindari Duplikasi Baris dan Kejutan Many-to-Many
Join tidak hanya “menambahkan kolom”—mereka juga bisa mengalikan baris. Itu biasanya perilaku yang benar, tetapi sering mengejutkan ketika total tiba-tiba berlipat ganda (atau lebih buruk).
Mengapa baris menggandakan
Sebuah join mengembalikan satu baris output untuk setiap pasangan baris yang cocok.
- One-to-many: Satu pelanggan bisa punya banyak pesanan. Jika Anda join
customerskeorders, setiap pelanggan mungkin muncul berkali-kali—sekali per order. - Many-to-many (jebakan nyata): Jika Anda join
orderskepaymentsdan setiap order dapat punya banyak pembayaran, Anda bisa mendapatkan banyak baris per order. Jika Anda juga join ke tabel "many" lain (sepertiorder_items), Anda bisa menghasilkan efek perkalian:payments × itemsper order.
Pre-aggregate sebelum join
Jika tujuan Anda “satu baris per pelanggan” atau “satu baris per order”, ringkas sisi "many" terlebih dahulu, lalu join.
-- One row per order from payments
WITH payment_totals AS (
SELECT
order_id,
SUM(amount) AS total_paid,
COUNT(*) AS payment_count
FROM payments
GROUP BY order_id
)
SELECT
o.order_id,
o.customer_id,
COALESCE(pt.total_paid, 0) AS total_paid,
COALESCE(pt.payment_count, 0) AS payment_count
FROM orders o
LEFT JOIN payment_totals pt
ON pt.order_id = o.order_id;
Ini menjaga bentuk join tetap dapat diprediksi: satu baris order tetap satu baris order.
DISTINCT adalah jalan terakhir
SELECT DISTINCT bisa membuat duplikasi terlihat beres, tetapi mungkin menyembunyikan masalah sebenarnya:
- Bisa menghapus baris yang sah secara diam-diam.
- Bisa menutupi kondisi join yang salah (mis. hilangnya bagian dari composite key).
- Bisa merusak total (terutama SUM dan COUNT).
Gunakan hanya ketika Anda yakin duplikasi murni tidak sengaja dan Anda mengerti penyebabnya.
Pemeriksaan cepat keamanan: validasi hitungan
Sebelum mempercayai hasil, bandingkan jumlah baris:
- Hitung baris di tabel utama Anda (mis. orders).
- Hitung baris setelah join.
- Jika jumlah melonjak tak terduga, periksa kunci mana yang menyebabkan banyak kecocokan dan putuskan apakah Anda perlu pra-agregasi atau jalur join lain.
Dasar-dasar Performa dan Cheat Sheet JOIN Singkat
JOIN sering disalahkan atas “query lambat”, tetapi penyebab nyata biasanya adalah berapa banyak data yang Anda minta untuk digabungkan, dan seberapa mudah database menemukan baris yang cocok.
Indexing (secara konseptual) dan mengapa membantu JOIN
Pikirkan index seperti daftar isi buku. Tanpa index, database mungkin harus memindai banyak baris untuk menemukan kecocokan pada kondisi JOIN Anda. Dengan index pada key join (mis. customers.customer_id dan orders.customer_id), database bisa langsung melompat ke baris relevan jauh lebih cepat.
Anda tidak perlu tahu detail internalnya untuk memakai ini dengan baik: jika sebuah kolom sering dipakai untuk mencocokkan baris (ON a.id = b.a_id), itu kandidat bagus untuk di-index.
Join pada key yang stabil (bukan nama atau email)
Sebisa mungkin, join pada identifier unik yang stabil:
- Baik:
customers.customer_id = orders.customer_id - Berisiko:
customers.email = orders.emailataucustomers.name = orders.name
Nama berubah dan bisa sama. Email bisa berubah, hilang, atau berbeda format. ID dirancang khusus untuk pencocokan konsisten, dan biasanya di-index.
Kurangi pekerjaan sejak awal
Dua kebiasaan membuat JOIN terasa lebih cepat:
- Pilih lebih sedikit kolom. Hindari
SELECT *saat join—kolom ekstra meningkatkan penggunaan memori dan jaringan. - Batasi baris sebelum atau selama JOIN. Filter sedini mungkin jika masuk akal.
Contoh: batasi orders dulu, lalu join:
SELECT c.customer_id, c.name, o.order_id, o.created_at
FROM customers c
JOIN (
SELECT order_id, customer_id, created_at
FROM orders
WHERE created_at >= DATE '2025-01-01'
) o
ON o.customer_id = c.customer_id;
Jika Anda iterasi pada query ini dalam proses membangun aplikasi (mis. membuat halaman laporan dengan PostgreSQL), alat seperti Koder.ai bisa mempercepat scaffolding—skema, endpoint, UI—sementara Anda tetap mengendalikan logika JOIN yang menentukan kebenaran.
Cheat sheet JOIN singkat
- INNER JOIN → hanya baris yang cocok di kedua tabel
- LEFT JOIN → semua baris dari tabel kiri, plus kecocokan dari kanan (yang tidak cocok menjadi
NULL) - RIGHT JOIN → semua baris dari tabel kanan, plus kecocokan dari kiri (
NULLketika hilang) - FULL OUTER JOIN → semua baris dari kedua tabel; kecocokan digabung, yang tidak cocok menunjukkan
NULL - CROSS JOIN → setiap kombinasi baris (ukuran berlipat; gunakan hati-hati)
- SELF JOIN → tabel yang di-join ke dirinya sendiri (berguna untuk hirarki dan perbandingan)
Pertanyaan umum
What is a SQL JOIN in plain English?
Sebuah SQL JOIN menggabungkan baris dari dua (atau lebih) tabel menjadi satu hasil dengan mencocokkan kolom terkait—biasanya primary key ke foreign key (misalnya, customers.customer_id = orders.customer_id). Ini adalah cara untuk “menghubungkan kembali” tabel yang dinormalisasi saat Anda butuh laporan, audit, atau analisis.
When should I use INNER JOIN?
Gunakan INNER JOIN saat Anda hanya menginginkan baris yang hubungan-nya ada di kedua tabel.
- Pelanggan tanpa pesanan tidak akan muncul.
- Pesanan yang merujuk ke customer yang hilang tidak akan muncul.
Ini ideal untuk “hubungan yang terkonfirmasi”, misalnya menampilkan hanya pelanggan yang benar-benar melakukan pembelian.
How do I find rows that don’t have a match in another table?
Gunakan LEFT JOIN ketika Anda butuh semua baris dari tabel utama (kiri) dan menambahkan data terkait dari tabel kanan jika ada.
Untuk menemukan “tidak ada kecocokan”, gabungkan lalu saring sisi kanan menjadi NULL:
SELECT c.customer_id, c.name
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.customer_id
WHERE o.order_id IS NULL;
Do I really need RIGHT JOIN, and how do I replace it with LEFT JOIN?
RIGHT JOIN menyertakan setiap baris dari tabel kanan dan mengisi kolom tabel kiri dengan NULL ketika tidak ada kecocokan. Banyak tim menghindarinya karena membaca “terbalik”.
Dalam kebanyakan kasus, Anda bisa menulis ulang sebagai LEFT JOIN dengan menukar urutan tabel:
FROM payments p
LEFT JOIN orders o ON o.order_id = p.order_id
What is FULL OUTER JOIN best used for?
Gunakan FULL OUTER JOIN untuk rekonsiliasi: Anda ingin melihat yang cocok, yang hanya ada di kiri, dan yang hanya ada di kanan dalam satu keluaran.
Ini bagus untuk audit seperti “pesanan tanpa pembayaran” dan “pembayaran tanpa pesanan”, karena sisi yang tidak cocok akan tampil sebagai kolom NULL.
What if my database doesn’t support FULL OUTER JOIN?
Beberapa basis data (terutama MySQL dan SQLite) tidak mendukung FULL OUTER JOIN langsung. Cara umum adalah menggabungkan dua query:
orders LEFT JOIN payments- lalu tambahkan baris yang hanya ada di sisi payments
Biasanya dilakukan dengan UNION (atau UNION ALL dengan penyaringan yang hati-hati) sehingga Anda mempertahankan record “kiri-saja” dan “kanan-saja”.
What is a CROSS JOIN, and when is it actually useful?
CROSS JOIN menghasilkan setiap kombinasi baris antara dua tabel (produk Kartesius). Berguna untuk menghasilkan skenario (mis. ukuran × warna) atau membuat grid kalender.
Hati-hati: jumlah baris cepat membengkak, jadi ini hanya praktis jika inputnya kecil dan terkontrol.
What is a SELF JOIN, and why do I need table aliases?
Self join adalah menggabungkan tabel ke dirinya sendiri untuk mengaitkan baris di dalam tabel yang sama (umum untuk hirarki seperti karyawan → manager).
Anda harus menggunakan alias untuk membedakan dua “salinan”:
FROM employees e
LEFT JOIN employees m
ON e.manager_id = m.id
What’s the difference between filtering in ON vs WHERE for joins?
ON mendefinisikan bagaimana baris dicocokkan saat join; WHERE memfilter setelah hasil join terbentuk. Dengan LEFT JOIN, kondisi WHERE pada tabel kanan bisa menghapus baris NULL yang membuatnya berperilaku seperti INNER JOIN.
Jika Anda ingin mempertahankan semua baris kiri namun membatasi baris kanan yang boleh cocok, taruh kondisi pada tabel kanan di ON.
Why do joins create duplicates, and how do I prevent double-counting?
Join dapat menggandakan baris ketika relasinya one-to-many atau many-to-many. Misalnya, sebuah pesanan dengan dua pembayaran akan muncul dua kali jika Anda menggabungkan orders ke payments.
Untuk menjaga “satu baris per order/pelanggan”, lakukan agregasi dulu di sisi many (mis. SUM(amount) grouped by order_id) lalu join. Gunakan DISTINCT hanya sebagai upaya terakhir karena bisa menyembunyikan masalah join dan merusak total.