FULL OUTER JOIN SQL: Gabungkan Semua Baris dari Dua Tabel
TL;DR
FULL OUTER JOIN adalah perintah SQL yang menggabungkan dua tabel dan menampilkan semua baris dari keduanya, baik yang punya pasangan maupun yang tidak. Baris yang gak nemu pasangan tetap muncul, dengan kolom dari tabel sebelahnya diisi NULL. Fungsi ini paling berguna buat rekonsiliasi: nemuin data yang cuma ada di satu tabel dan hilang di tabel satunya. PostgreSQL dan SQL Server mendukungnya langsung, sementara MySQL harus ditiru pakai kombinasi LEFT JOIN dan RIGHT JOIN yang digabung UNION.
FULL OUTER JOIN nampilin semua baris dari dua tabel sekaligus. Yang punya pasangan digabung jadi satu baris. Yang gak punya pasangan tetap muncul, tapi kolom dari tabel sebelahnya diisi NULL.
Ini JOIN yang paling jarang dipakai sehari-hari. Tapi pas lagi rekonsiliasi data — misalnya stok sistem gak cocok sama stok fisik — dia satu-satunya yang bisa nunjukin dua arah selisihnya sekaligus.
Apa itu FULL OUTER JOIN di SQL?
FULL OUTER JOIN adalah jenis JOIN yang mengembalikan semua baris dari tabel kiri dan semua baris dari tabel kanan. Baris yang cocok berdasarkan kondisi ON digabung. Baris yang gak nemu pasangan tetap ikut, dengan kolom dari tabel lawannya diisi NULL.
Sintaksnya:
SELECT kolom
FROM tabel_kiri a
FULL OUTER JOIN tabel_kanan b
ON a.kunci = b.kunci;
Kata OUTER boleh dibuang. FULL JOIN artinya sama persis. Ini juga berlaku di LEFT JOIN dan RIGHT JOIN.
Gimana cara kerja FULL OUTER JOIN?
Gampangnya, dia gabungan dari LEFT JOIN dan RIGHT JOIN, tapi baris yang muncul di dua-duanya gak digandain.
Ada tiga jenis baris di hasilnya:
- Cocok di dua tabel — semua kolom keisi normal
- Cuma ada di tabel kiri — kolom dari tabel kanan jadi NULL
- Cuma ada di tabel kanan — kolom dari tabel kiri jadi NULL
Bandingin sama JOIN yang lain:
| Jenis JOIN | Baris yang cocok | Baris yatim kiri | Baris yatim kanan |
|---|---|---|---|
| INNER JOIN | Ikut | Dibuang | Dibuang |
| LEFT JOIN | Ikut | Ikut (NULL) | Dibuang |
| RIGHT JOIN | Ikut | Dibuang | Ikut (NULL) |
| FULL OUTER JOIN | Ikut | Ikut (NULL) | Ikut (NULL) |
Cuma FULL OUTER JOIN yang gak buang apa pun.
Contoh kasus: rekonsiliasi stok opname toko_berkah
Ini kasus yang bikin FULL OUTER JOIN kepakai beneran.
Toko Berkah punya dua tabel. produk berisi 5 barang yang terdaftar di sistem. stok_opname berisi hasil hitung fisik gudang akhir Januari.
Bikin tabelnya dulu:
CREATE TABLE produk (
produk_id INT PRIMARY KEY,
nama_produk VARCHAR(100) NOT NULL,
kategori VARCHAR(50),
harga DECIMAL(12,2)
);
CREATE TABLE stok_opname (
opname_id INT PRIMARY KEY,
produk_id INT,
tanggal DATE,
qty_fisik INT
);
Isi datanya:
INSERT INTO produk (produk_id, nama_produk, kategori, harga) VALUES
(1, 'Beras Pandan Wangi 5kg', 'Sembako', 78000),
(2, 'Minyak Goreng 2L', 'Sembako', 36000),
(3, 'Kopi Gayo 250g', 'Minuman', 65000),
(4, 'Gula Aren 500g', 'Sembako', 28000),
(5, 'Teh Melati 100g', 'Minuman', 15000);
INSERT INTO stok_opname (opname_id, produk_id, tanggal, qty_fisik) VALUES
(101, 1, '2026-01-31', 42),
(102, 2, '2026-01-31', 18),
(103, 3, '2026-01-31', 7),
(104, 9, '2026-01-31', 25),
(105, 12, '2026-01-31', 3);
Liat baris terakhir. Ada produk_id 9 dan 12 di hasil opname, padahal di tabel produk cuma ada ID 1 sampai 5. Barang siluman.
Sekarang gabungin:
SELECT
p.produk_id AS id_di_master,
p.nama_produk,
s.produk_id AS id_di_opname,
s.qty_fisik
FROM produk p
FULL OUTER JOIN stok_opname s
ON p.produk_id = s.produk_id
ORDER BY COALESCE(p.produk_id, s.produk_id);
Hasilnya:
| id_di_master | nama_produk | id_di_opname | qty_fisik |
|---|---|---|---|
| 1 | Beras Pandan Wangi 5kg | 1 | 42 |
| 2 | Minyak Goreng 2L | 2 | 18 |
| 3 | Kopi Gayo 250g | 3 | 7 |
| 4 | Gula Aren 500g | NULL | NULL |
| 5 | Teh Melati 100g | NULL | NULL |
| NULL | NULL | 9 | 25 |
| NULL | NULL | 12 | 3 |
Dua masalah langsung kelihatan dari satu query:
- Gula Aren dan Teh Melati terdaftar di sistem tapi gak pernah dihitung waktu opname
- Ada 28 unit barang di gudang (25 + 3) yang gak punya master data — kemungkinan hasil salah input kode
Coba pakai INNER JOIN. Kamu cuma dapat 3 baris pertama, dan semua masalah tadi hilang tanpa jejak.
Tampilkan yang bermasalah aja
Buat laporan rekonsiliasi, saring yang cocok:
SELECT
COALESCE(p.produk_id, s.produk_id) AS produk_id,
COALESCE(p.nama_produk, '(tidak ada di master)') AS nama_produk,
CASE
WHEN s.produk_id IS NULL THEN 'Belum di-opname'
WHEN p.produk_id IS NULL THEN 'Barang siluman'
END AS masalah
FROM produk p
FULL OUTER JOIN stok_opname s
ON p.produk_id = s.produk_id
WHERE p.produk_id IS NULL
OR s.produk_id IS NULL;
Empat baris keluar. Itu daftar kerjaan tim gudang besok pagi.
COALESCE di sini penting — dia ngambil nilai pertama yang bukan NULL, jadi kolom produk_id selalu keisi dari sisi mana pun datanya datang. Detailnya ada di panduan COALESCE.
Gimana cara bikin FULL OUTER JOIN di MySQL?
MySQL gak punya sintaks FULL OUTER JOIN. Sampai versi 8.4 pun belum ada.
Tirunya pakai LEFT JOIN plus RIGHT JOIN yang disatuin UNION:
SELECT p.produk_id, p.nama_produk, s.qty_fisik
FROM produk p
LEFT JOIN stok_opname s ON p.produk_id = s.produk_id
UNION
SELECT p.produk_id, p.nama_produk, s.qty_fisik
FROM produk p
RIGHT JOIN stok_opname s ON p.produk_id = s.produk_id;
UNION otomatis buang baris duplikat, jadi baris yang cocok di dua tabel gak muncul dua kali. Kalau kamu pakai UNION ALL tanpa filter, baris yang cocok bakal kegandain.
PostgreSQL dan SQL Server sih dukung penuh. Kamu bisa cek di dokumentasi resmi PostgreSQL soal table expressions.
Kesalahan umum waktu pakai FULL OUTER JOIN
1. Filter kolom tabel kanan di WHERE
Ini yang paling sering.
-- SALAH: FULL OUTER JOIN-nya jadi percuma
FROM produk p
FULL OUTER JOIN stok_opname s ON p.produk_id = s.produk_id
WHERE s.qty_fisik > 0;
Baris yatim dari tabel produk punya qty_fisik = NULL. NULL gak lolos perbandingan > 0, jadi semuanya kebuang. Hasil akhirnya sama kayak INNER JOIN.
Kalau kondisinya buat nyaring pasangan, taruh di ON. Kalau buat nyaring hasil akhir, baru di WHERE.
2. Ambil kolom kunci dari satu sisi doang
SELECT p.produk_id bakal NULL buat baris yang cuma ada di tabel kanan. Bungkus pakai COALESCE(p.produk_id, s.produk_id) biar selalu keisi.
3. ORDER BY kolom yang bisa NULL
Baris NULL numpuk di ujung dan bikin hasilnya susah dibaca. Sort pakai COALESCE juga.
4. Kolom kunci gak diindeks
FULL OUTER JOIN harus scan dari dua sisi. Tanpa index di kolom ON, query yang tadinya 200 milidetik bisa jadi 30 detik di tabel ratusan ribu baris.
FAQ
Apa bedanya FULL OUTER JOIN dan INNER JOIN?
INNER JOIN cuma nampilin baris yang punya pasangan di kedua tabel. Baris yatim langsung dibuang tanpa pemberitahuan. FULL OUTER JOIN nampilin semua baris dari dua tabel, termasuk yang gak punya pasangan, dan ngisi kolom yang kosong pakai NULL. Kalau tujuan kamu nemuin data yang hilang, INNER JOIN justru nyembunyiin masalahnya.
Kenapa MySQL nggak punya FULL OUTER JOIN?
MySQL emang gak pernah mengimplementasikan sintaks FULL OUTER JOIN, sampai versi 8.4 pun belum ada. Solusinya, gabungkan LEFT JOIN dan RIGHT JOIN pakai UNION. UNION otomatis buang baris duplikat, jadi baris yang cocok di kedua tabel gak muncul dua kali.
FULL OUTER JOIN sama FULL JOIN itu beda?
Sama persis. Kata OUTER itu opsional di standar SQL, jadi FULL JOIN dan FULL OUTER JOIN dieksekusi identik oleh PostgreSQL maupun SQL Server. Aku sendiri lebih suka nulis lengkap pakai OUTER, biar orang yang baca query-nya nanti langsung tau itu bukan INNER JOIN.
Gimana cara filter hasil FULL OUTER JOIN biar cuma nampilin yang nggak match?
Tambahin WHERE yang ngecek NULL di kolom kunci kedua tabel: WHERE a.id IS NULL OR b.id IS NULL. Baris yang cocok pasti punya nilai di dua kolom itu, jadi otomatis kesaring keluar. Sisanya cuma baris yatim dari kiri dan kanan. Ini pola standar buat laporan rekonsiliasi.
Apakah FULL OUTER JOIN lambat?
Lebih berat dari INNER JOIN, soalnya database harus nyimpen baris yang gak match dari dua sisi sekaligus. Di data kecil sampai menengah selisihnya gak kerasa. Yang bikin lambat biasanya bukan JOIN-nya, tapi kolom kunci yang gak diindeks. Pastiin kolom di klausa ON punya index di kedua tabel.
Penutup
Yang perlu diinget:
- FULL OUTER JOIN gak buang baris apa pun. Yang gak punya pasangan tetap muncul dengan NULL.
- Kepakainya buat rekonsiliasi — nemuin selisih dua arah dalam satu query.
- Filter di WHERE bisa diam-diam bikin dia balik jadi INNER JOIN. Hati-hati.
Query di atas bisa langsung kamu copy dan jalanin. Ganti nama tabelnya sama data kerjaan kamu, terus liat berapa baris yatim yang selama ini gak keitung.
Mau langsung praktek tanpa install database? Latihan JOIN interaktifnya ada di halaman fungsi FULL OUTER JOIN. Definisi istilah NULL dan baris yatim juga aku tulis di glosarium NULL.
Lanjut baca: CROSS JOIN SQL — satu-satunya JOIN yang gak butuh klausa ON sama sekali.
Mau praktek langsung? Mulai latihan SQL gratis
Latihan interaktif, langsung di browser.
Artikel Terkait
7 SQL Client Gratis Terbaik buat Analis Data (2026)
Nggak perlu bayar buat nulis query. Ini 7 SQL client gratis yang beneran dipakai analis, plus kelebihan dan kekurangannya masing-masing.
SQL Style Guide: Cara Nulis Query yang Enak Dibaca Tim
Sepuluh aturan SQL style guide yang bikin query kamu kebaca sama tim — dan sama kamu sendiri 6 bulan lagi. Lengkap dengan contoh sebelum-sesudah.
Take-Home Test SQL Data Analyst: Contoh Soal + Cara Ngerjainnya
Contoh soal take-home test SQL yang beneran dipakai perusahaan, dikerjain step by step — dari baca soal, nulis query, sampai nyusun insight yang bikin recruiter nengok.