Topik ini membahas outer join, yaitu join yang mempertahankan baris tanpa pasangan alih-alih membuangnya, dengan LEFT JOIN sebagai bentuk yang paling sering dipakai. Pertanyaan yang dilayani outer join selalu berbunyi serupa, misalnya kategori apa saja yang belum memiliki buku, dan pertanyaan semacam itu mustahil dijawab INNER JOIN.
Blok penyiapan dataset#
# siapkan ulang tabel dan data contoh, ditambah satu kategori kosong
PGOPTIONS='-c client_min_messages=warning' psql -q -U postgres -d perpustakaan <<'SQL'
DROP TABLE IF EXISTS peminjaman, buku, anggota, kategori CASCADE;
CREATE TABLE kategori (id SERIAL PRIMARY KEY, nama TEXT NOT NULL UNIQUE);
CREATE TABLE anggota (id SERIAL PRIMARY KEY, nama TEXT NOT NULL, email TEXT UNIQUE, bergabung DATE NOT NULL, aktif BOOLEAN NOT NULL DEFAULT TRUE);
CREATE TABLE buku (id SERIAL PRIMARY KEY, judul TEXT NOT NULL, penulis TEXT NOT NULL, tahun INTEGER NOT NULL, kategori_id INTEGER REFERENCES kategori(id));
CREATE TABLE peminjaman (id SERIAL PRIMARY KEY, anggota_id INTEGER NOT NULL REFERENCES anggota(id), buku_id INTEGER NOT NULL REFERENCES buku(id), tanggal_pinjam DATE NOT NULL, tanggal_kembali DATE);
INSERT INTO kategori (nama) VALUES ('Pemrograman'), ('Sains'), ('Sejarah'), ('Fiksi'), ('Statistika'), ('Filsafat');
INSERT INTO anggota (nama, email, bergabung, aktif) VALUES ('Ali Rahman', '[email protected]', '2024-01-15', TRUE), ('Budi Santoso', '[email protected]', '2024-03-02', TRUE), ('Citra Kirana', '[email protected]', '2024-05-20', FALSE), ('Dewi Lestari', '[email protected]', '2024-08-11', TRUE), ('Eka Pratama', NULL, '2025-01-30', TRUE);
INSERT INTO buku (judul, penulis, tahun, kategori_id) VALUES ('Dasar Basis Data', 'Sari Wulandari', 2021, 1), ('Jaringan Komputer', 'Ahmad Fauzi', 2019, 1), ('Fisika untuk Pemula', 'Rina Marlina', 2020, 2), ('Kimia Organik', 'Rina Marlina', 2022, 2), ('Sejarah Nusantara', 'Bambang Wijaya', 2018, 3), ('Cerita Kota Lama', 'Nur Aisyah', 2023, 4), ('Statistika Terapan', 'Sari Wulandari', 2022, 5), ('Algoritma dan Struktur Data', 'Ahmad Fauzi', 2021, 1);
INSERT INTO peminjaman (anggota_id, buku_id, tanggal_pinjam, tanggal_kembali) VALUES (1, 1, '2025-03-01', '2025-03-15'), (1, 7, '2025-03-05', '2025-03-20'), (2, 3, '2025-03-10', NULL), (3, 5, '2025-02-20', '2025-03-01'), (2, 1, '2025-04-02', NULL), (4, 6, '2025-04-07', NULL), (5, 8, '2025-04-09', '2025-04-25'), (4, 2, '2025-05-01', NULL);
SQLDataset kali ini menambahkan kategori Filsafat yang sengaja tanpa buku, sehingga baris tanpa pasangan benar-benar ada untuk diuji.
LEFT JOIN menjaga baris kiri#
LEFT JOIN mengembalikan seluruh baris tabel kiri, dan baris tanpa pasangan tetap muncul dengan kolom tabel kanan yang bernilai NULL. Query berikut mendaftar setiap kategori beserta judul bukunya.
# seluruh kategori beserta bukunya, termasuk yang kosong
psql -U postgres -d perpustakaan -c "SELECT k.nama AS kategori, b.judul FROM kategori k LEFT JOIN buku b ON b.kategori_id = k.id ORDER BY k.id, b.id;" kategori | judul
-------------+-----------------------------
Pemrograman | Dasar Basis Data
Pemrograman | Jaringan Komputer
Pemrograman | Algoritma dan Struktur Data
Sains | Fisika untuk Pemula
Sains | Kimia Organik
Sejarah | Sejarah Nusantara
Fiksi | Cerita Kota Lama
Statistika | Statistika Terapan
Filsafat |
(9 rows)Kategori Filsafat muncul satu baris dengan judul kosong, sementara Pemrograman muncul tiga kali karena tiga bukunya. Pola ini disebut pola penemuan yang hilang, yaitu baris kiri yang kanannya kosong sekaligus menjadi daftar lengkap dan daftar yang belum terisi.
Menyaring yang tidak berpasangan#
Baris tanpa pasangan dikenali dengan menguji kolom kunci tabel kanan terhadap NULL, dan pola ini menjadi query standar untuk menemukan yang belum berelasi.
# kategori yang belum memiliki satu buku pun
psql -U postgres -d perpustakaan -c "SELECT k.nama FROM kategori k LEFT JOIN buku b ON b.kategori_id = k.id WHERE b.id IS NULL;" nama
----------
Filsafat
(1 row)Hanya Filsafat yang lolos, karena hanya baris itu yang kolom bukunya kosong. Pembanding INNER JOIN pada query yang sama mengembalikan nol baris, perbedaan yang menegaskan mengapa outer join dibutuhkan untuk pertanyaan kelengkapannya.
Jebakan WHERE pada outer join#
Syarat yang seharusnya milik ON tetapi ditulis di WHERE dapat menghilangkan baris yang justru ingin dijaga. Syarat pada tabel kanan yang bernilai NULL gagal setiap uji perbandingan, sehingga seluruh baris tanpa pasangan tereliminasi diam-diam dan LEFT JOIN berperilaku seperti INNER JOIN.
# syarat kanan di WHERE menghilangkan baris tanpa pasangan
psql -U postgres -d perpustakaan -c "SELECT k.nama, b.judul FROM kategori k LEFT JOIN buku b ON b.kategori_id = k.id WHERE b.tahun >= 2022 ORDER BY k.id, b.id;" nama | judul
------------+--------------------
Sains | Kimia Organik
Fiksi | Cerita Kota Lama
Statistika | Statistika Terapan
(3 rows)Keluaran hanya memuat kategori yang berisi buku terbitan 2022 ke atas, dan Filsafat hilang padahal LEFT JOIN-nya berniat menjaganya. Bentuk yang tepat menaruh syarat kanan pada ON, yaitu LEFT JOIN buku b ON b.kategori_id = k.id AND b.tahun >= 2022, sehingga Filsafat kembali muncul dengan kolom kanan yang kosong. Aturannya ringkas, yaitu syarat yang memperluas pasangan ditaruh di ON, sedangkan syarat yang menyaring hasil akhir ditaruh di WHERE.
RIGHT JOIN dan kebiasaan menulis kiri#
RIGHT JOIN mempertahankan seluruh baris tabel kanan, sebagaimana LEFT JOIN yang menjaga tabel kiri, dan keduanya saling tertukar dengan membalik urutan tabelnya. Query kategori kosong di atas dapat ditulis ulang sebagai FROM buku b RIGHT JOIN kategori k ON b.kategori_id = k.id dengan hasil yang identik.
Dalam praktik, RIGHT JOIN jarang dipakai karena baris query dibaca kiri ke kanan, sehingga penulisannya diubah menjadi LEFT JOIN dengan urutan tabel yang dibalik. FULL OUTER JOIN yang menjaga kedua sisi sekaligus menjadi pembahasan topik tersendiri.
Kesalahan umum#
Kesalahan pertama adalah syarat kanan di WHERE yang mengeliminasi baris tanpa pasangan, jebakan yang biasanya baru ditemukan ketika laporannya kehilangan data, dan penangkalnya adalah memisahkan syarat ON dari syarat WHERE sejak awal. Kesalahan kedua adalah menguji kolom kanan dengan IS NULL tanpa memastikan kolom itu memang boleh NULL secara sah pada pasangan yang ada. Kolom NOT NULL pada tabel kanan menjadi penanda yang andal, sedangkan kolom yang boleh NULL membuat hasil uji ambigu.
Kesalahan ketiga adalah mengira LEFT JOIN menjamin satu baris per baris kiri. Baris kiri dengan banyak pasangan muncul banyak kali, dan yang dijamin hanya kelengkapannya, sehingga penghitungan yang benar memakai COUNT kolom kanan yang mengabaikan NULL, sebagaimana dibahas pada modul agregasi.
Ringkasan#
- LEFT JOIN mempertahankan seluruh baris kiri, dan baris tanpa pasangan muncul dengan kolom kanan bernilai NULL.
- Pola
WHERE kolom_kanan IS NULLmenjadi cara baku menemukan baris yang belum berelasi. - Syarat pada tabel kanan yang ditulis di WHERE mengeliminasi baris tanpa pasangan, sehingga syarat pemilihan pasangan ditaruh di ON.
- RIGHT JOIN setara LEFT JOIN dengan urutan tabel yang dibalik, dan kebiasaan menulis tetap LEFT JOIN.
Referensi#
- Joins Between Tables — PostgreSQL Documentation — Bagian tutorial resmi tentang join antartabel.
- Joined Tables — PostgreSQL Documentation — Referensi outer join beserta urutan penilaiannya.
- Comparison Functions and Operators — PostgreSQL Documentation — Predikat IS NULL yang dipakai untuk menyaring baris tanpa pasangan.