Chapter sebelumnya mempertahankan seluruh pelanggan, tetapi pesanan walk-in 105 tetap tak pernah tampil: tabel kanan selalu jadi pihak yang rela dibuang. RIGHT JOIN membalik peran itu — seluruh baris tabel kanan dipertahankan, dan baris kiri yang tanpa pasangan ditambal NULL — sehingga pesanan tanpa akun pelanggan akhirnya kelihatan. Di atas kertas kedua join hanyalah cermin satu sama lain, dan memang begitu: setiap RIGHT JOIN bisa ditulis ulang sebagai LEFT JOIN dengan menukar posisi tabel. Chapter ini membuktikan kesetaraan itu dengan query nyata, lalu memperlihatkan satu situasi tempat RIGHT JOIN justru terbaca lebih alami: ketika analisis dimulai dari tabel yang difilter, sementara tabel lain yang harus tetap utuh.
Cermin dari LEFT JOIN #
RIGHT JOIN mengevaluasi kondisi ON dengan cara yang sama seperti kedua chapter sebelumnya; satu-satunya perbedaan adalah pihak yang dijamin tampil. Kata RIGHT merujuk tabel yang ditulis setelah klausa join — tabel kedua — dan tabel itulah yang barisnya tidak pernah hilang:
| Bentuk penulisan | Tabel yang dipertahankan penuh | Tabel yang barisnya bisa hilang |
|---|---|---|
a LEFT JOIN b | a (kiri) | b |
a RIGHT JOIN b | b (kanan) | a |
| Baris tanpa pasangan di sisi dipertahankan | tampil, sisi lawan ditambal NULL | — |
Skema contoh tidak berubah, sehingga hasil tiap chapter bisa diperbandingkan langsung:
-- Skema contoh: pelanggan 4 baris, pesanan 5 baris
CREATE TABLE pelanggan (
id integer PRIMARY KEY,
nama text NOT NULL,
kota text NOT NULL,
email text NOT NULL UNIQUE
);
CREATE TABLE pesanan (
id integer PRIMARY KEY,
pelanggan_id integer REFERENCES pelanggan (id),
tanggal date NOT NULL,
total numeric(12, 2) NOT NULL
);
INSERT INTO pelanggan (id, nama, kota, email) VALUES
(1, 'Budi Santoso', 'Bandung', '[email protected]'),
(2, 'Siti Rahayu', 'Jakarta', '[email protected]'),
(3, 'Andi Wijaya', 'Surabaya', '[email protected]'),
(4, 'Dewi Lestari', 'Medan', '[email protected]');
INSERT INTO pesanan (id, pelanggan_id, tanggal, total) VALUES
(101, 1, DATE '2026-01-05', 150000.00),
(102, 1, DATE '2026-01-18', 75000.00),
(103, 2, DATE '2026-01-09', 220000.00),
(104, 4, DATE '2026-02-02', 50000.00),
(105, NULL, DATE '2026-02-10', 30000.00);
SELECT * FROM pelanggan ORDER BY id;
SELECT * FROM pesanan ORDER BY id;Output yang Dihasilkan:
CREATE TABLE
CREATE TABLE
INSERT 0 4
INSERT 0 5
id | nama | kota | email
----+--------------+----------+------------------
1 | Budi Santoso | Bandung | [email protected]
2 | Siti Rahayu | Jakarta | [email protected]
3 | Andi Wijaya | Surabaya | [email protected]
4 | Dewi Lestari | Medan | [email protected]
(4 rows)
id | pelanggan_id | tanggal | total
-----+--------------+------------+-----------
101 | 1 | 2026-01-05 | 150000.00
102 | 1 | 2026-01-18 | 75000.00
103 | 2 | 2026-01-09 | 220000.00
104 | 4 | 2026-02-02 | 50000.00
105 | | 2026-02-10 | 30000.00
(5 rows)Pesanan Tanpa Pelanggan Kini Tampil #
Menulis pelanggan RIGHT JOIN pesanan menempatkan pesanan sebagai pihak yang dipertahankan. Pesanan 105 yang selama ini hilang akhirnya muncul — dengan kolom pelanggan yang seluruhnya NULL:
-- RIGHT JOIN: seluruh baris pesanan tampil, kolom pelanggan ditambal NULL bila kosong
SELECT p.id AS pelanggan, p.nama, ps.id AS pesanan, ps.tanggal, ps.total
FROM pelanggan p
RIGHT JOIN pesanan ps ON ps.pelanggan_id = p.id
ORDER BY ps.id;Output yang Dihasilkan:
pelanggan | nama | pesanan | tanggal | total
-----------+--------------+---------+------------+-----------
1 | Budi Santoso | 101 | 2026-01-05 | 150000.00
1 | Budi Santoso | 102 | 2026-01-18 | 75000.00
2 | Siti Rahayu | 103 | 2026-01-09 | 220000.00
4 | Dewi Lestari | 104 | 2026-02-02 | 50000.00
| | 105 | 2026-02-10 | 30000.00
(5 rows)Lima baris hasil mencerminkan lima baris pesanan:
- Baris 101–104 — pesanan berpelanggan tampil normal, kolom
pelanggandannamaterisi dari pasangannya. - Baris
| (kosong) | (kosong) | 105 | 2026-02-10 | 30000.00— pesanan walk-in tanpa akun. Nilaipelanggan_idNULL tidak pernah cocok denganp.idmana pun, jadi seluruh kolom sisi kiri ditambal NULL. - Andi Wijaya tidak muncul — kini giliran tabel kiri yang tunduk pada aturan pembuangan: pelanggan tanpa pesanan hilang dari hasil, persis kebalikan dari chapter LEFT JOIN .
Ketika membaca hasil RIGHT JOIN, kebiasaan dari chapter sebelumnya perlu dibalik juga: baris dengan kolom pelanggan kosong bukan berarti “pelanggan bernama kosong”, melainkan “pesanan ini memang tidak memiliki pasangan pelanggan”.
Menulis Ulang sebagai LEFT JOIN #
Kedua query berikut menghasilkan lima baris yang identik — kata kuncinya hanya bertukar tempat:
-- Bentuk RIGHT JOIN: pesanan dipertahankan
SELECT p.nama, ps.id AS pesanan, ps.total
FROM pelanggan p
RIGHT JOIN pesanan ps ON ps.pelanggan_id = p.id
ORDER BY ps.id;
-- Bentuk LEFT JOIN yang setara: cukup tukar posisi kedua tabel
SELECT p.nama, ps.id AS pesanan, ps.total
FROM pesanan ps
LEFT JOIN pelanggan p ON ps.pelanggan_id = p.id
ORDER BY ps.id;Output yang Dihasilkan:
nama | pesanan | total
--------------+---------+-----------
Budi Santoso | 101 | 150000.00
Budi Santoso | 102 | 75000.00
Siti Rahayu | 103 | 220000.00
Dewi Lestari | 104 | 50000.00
| 105 | 30000.00
(5 rows)
nama | pesanan | total
--------------+---------+-----------
Budi Santoso | 101 | 150000.00
Budi Santoso | 102 | 75000.00
Siti Rahayu | 103 | 220000.00
Dewi Lestari | 104 | 50000.00
| 105 | 30000.00
(5 rows)Perbandingannya per klause:
- Bentuk pertama —
pelanggan p RIGHT JOIN pesanan psmempertahankanpesanankarena berada di kanan. - Bentuk kedua —
pesanan ps LEFT JOIN pelanggan pmempertahankanpesanankarena berada di kiri. KondisiONtidak berubah sama sekali; hanya urutan tabel yang bertukar. - Hasil keduanya identik — lima baris yang sama, termasuk baris 105 dengan
namakosong. Join sama sekali tidak peduli tabel mana yang “lebih penting”; yang dilihat hanya sisi yang dipertahankan.
Inilah alasan banyak tim standar menulis semua outer join sebagai LEFT JOIN: satu arah baca berarti satu kebiasaan mental, dan reviewer tidak perlu membolak-balik pertanyaan “sisi mana yang dipertahankan?” tiap membaca query. PostgreSQL sendiri tidak membedakan performa keduanya — optimizer memperlakukan kedua bentuk sebagai query yang sama.
Kapan RIGHT JOIN Terbaca Alami #
Ada situasi ketika bentuk RIGHT justru lebih jujur terhadap alur berpikir: narasi query dimulai dari tabel yang sedang dianalisis dan difilter, sementara tabel yang harus utuh datang belakangan. Contohnya: “periksa semua pesanan besar, tapi pastikan setiap pelanggan tetap terlihat walau pesanannya kecil”:
-- Pertanyaan dimulai dari pesanan besar, tetapi semua pelanggan harus tetap tampil
SELECT p.id, p.nama, ps.id AS pesanan, ps.total
FROM pesanan ps
RIGHT JOIN pelanggan p
ON p.id = ps.pelanggan_id AND ps.total >= 100000.00
ORDER BY p.id, ps.id;Output yang Dihasilkan:
id | nama | pesanan | total
----+--------------+---------+-----------
1 | Budi Santoso | 101 | 150000.00
2 | Siti Rahayu | 103 | 220000.00
3 | Andi Wijaya | |
4 | Dewi Lestari | |
(4 rows)Bedah penempatannya:
FROM pesanan ps— tabel yang menjadi subjek analisis ditulis lebih dulu, sesuai urutan narasi: “pesanan besar mana yang masuk?”RIGHT JOIN pelanggan p—pelangganberada di kanan sehingga seluruh barisnya dipertahankan, walau pesanannya tidak lolos ambang.ON ... AND ps.total >= 100000.00— ambang total ditaruh diON, bukan diWHERE, mengikuti aturan dari chapter LEFT JOIN : filter tabel non-dipertahankan harus ikut menentukan kecocokan pasangan, bukan menyaring hasil akhir. Ditaruh diWHERE, baris Andi dan Dewi akan ikut terbuang.
Empat baris hasil menutup cerita dengan rapi: Budi membawa pesanan 101, Siti membawa 103, sementara Andi dan Dewi tampil tanpa pesanan yang lolos ambang. Versi LEFT JOIN-nya (FROM pelanggan p LEFT JOIN pesanan ps ON ... AND ps.total >= 100000.00) menghasilkan baris yang sama — pilihan bentuk murni soal sisi mana yang membuat query terbaca sesuai narasi.
LEFT JOIN dan RIGHT JOIN masing-masing melindungi satu sisi. Pertanyaan berikutnya melindungi keduanya sekaligus: bagaimana menampilkan baris yang tidak berpasangan dari kedua tabel — pelanggan tanpa pesanan dan pesanan tanpa pelanggan dalam satu hasil? Jawabannya adalah FULL OUTER JOIN, chapter yang melengkapi keluarga outer join ini.
Referensi #
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
buat-tabel.sql
Unduh berkas-- Skema contoh: pelanggan 4 baris, pesanan 5 baris
CREATE TABLE pelanggan (
id integer PRIMARY KEY,
nama text NOT NULL,
kota text NOT NULL,
email text NOT NULL UNIQUE
);
CREATE TABLE pesanan (
id integer PRIMARY KEY,
pelanggan_id integer REFERENCES pelanggan (id),
tanggal date NOT NULL,
total numeric(12, 2) NOT NULL
);
INSERT INTO pelanggan (id, nama, kota, email) VALUES
(1, 'Budi Santoso', 'Bandung', '[email protected]'),
(2, 'Siti Rahayu', 'Jakarta', '[email protected]'),
(3, 'Andi Wijaya', 'Surabaya', '[email protected]'),
(4, 'Dewi Lestari', 'Medan', '[email protected]');
INSERT INTO pesanan (id, pelanggan_id, tanggal, total) VALUES
(101, 1, DATE '2026-01-05', 150000.00),
(102, 1, DATE '2026-01-18', 75000.00),
(103, 2, DATE '2026-01-09', 220000.00),
(104, 4, DATE '2026-02-02', 50000.00),
(105, NULL, DATE '2026-02-10', 30000.00);
SELECT * FROM pelanggan ORDER BY id;
SELECT * FROM pesanan ORDER BY id;
right-berguna.sql
Unduh berkas-- Pertanyaan dimulai dari pesanan besar, tetapi semua pelanggan harus tetap tampil
SELECT p.id, p.nama, ps.id AS pesanan, ps.total
FROM pesanan ps
RIGHT JOIN pelanggan p
ON p.id = ps.pelanggan_id AND ps.total >= 100000.00
ORDER BY p.id, ps.id;
right-dasar.sql
Unduh berkas-- RIGHT JOIN: seluruh baris pesanan tampil, kolom pelanggan ditambal NULL bila kosong
SELECT p.id AS pelanggan, p.nama, ps.id AS pesanan, ps.tanggal, ps.total
FROM pelanggan p
RIGHT JOIN pesanan ps ON ps.pelanggan_id = p.id
ORDER BY ps.id;
tulis-ulang.sql
Unduh berkas-- Bentuk RIGHT JOIN: pesanan dipertahankan
SELECT p.nama, ps.id AS pesanan, ps.total
FROM pelanggan p
RIGHT JOIN pesanan ps ON ps.pelanggan_id = p.id
ORDER BY ps.id;
-- Bentuk LEFT JOIN yang setara: cukup tukar posisi kedua tabel
SELECT p.nama, ps.id AS pesanan, ps.total
FROM pesanan ps
LEFT JOIN pelanggan p ON ps.pelanggan_id = p.id
ORDER BY ps.id;