Topik ini membuka modul join dengan INNER JOIN, gabungan yang hanya mempertahankan pasangan baris yang cocok menurut syaratnya. Dataset perpustakaan dipisah ke beberapa tabel sesuai prinsip model relasional, dan join menyatukannya kembali pada saat dibaca, sehingga kelima topik modul ini memakai dataset yang sama.
Blok penyiapan dataset#
# siapkan ulang tabel dan data contoh untuk topik ini
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');
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);
SQLMenggabungkan dua tabel#
INNER JOIN menyandingkan setiap baris tabel kiri dengan baris tabel kanan yang memenuhi syarat ON, dan baris tanpa pasangan dibuang. Query berikut menyandingkan catatan peminjaman dengan nama peminjamnya, karena tabel peminjaman hanya menyimpan id anggota.
# peminjaman beserta nama peminjamnya
psql -U postgres -d perpustakaan -c "SELECT peminjaman.id, anggota.nama, peminjaman.tanggal_pinjam FROM peminjaman INNER JOIN anggota ON peminjaman.anggota_id = anggota.id ORDER BY peminjaman.id;" id | nama | tanggal_pinjam
----+--------------+----------------
1 | Ali Rahman | 2025-03-01
2 | Ali Rahman | 2025-03-05
3 | Budi Santoso | 2025-03-10
4 | Citra Kirana | 2025-02-20
5 | Budi Santoso | 2025-04-02
6 | Dewi Lestari | 2025-04-07
7 | Eka Pratama | 2025-04-09
8 | Dewi Lestari | 2025-05-01
(8 rows)Delapan catatan peminjaman semuanya menemukan pasangan anggotanya, sehingga jumlah baris hasil sama dengan jumlah peminjaman. Nama tabel menjadi awalan setiap kolom, kebiasaan yang mencegah ambiguitas sekaligus menjelaskan asal setiap kolom kepada pembaca query.
Alias tabel untuk keterbacaan#
Query join lazimnya memakai alias pendek, yaitu huruf yang mewakili tabelnya, sehingga syarat ON dan daftar kolom tetap ringkas. Alias juga wajib ketika satu tabel dipakai dua kali, sebagaimana topik self-join.
# join dengan alias satu huruf
psql -U postgres -d perpustakaan -c "SELECT p.id, b.judul, k.nama AS kategori FROM peminjaman p JOIN buku b ON p.buku_id = b.id JOIN kategori k ON b.kategori_id = k.id WHERE p.tanggal_kembali IS NULL ORDER BY p.id;" id | judul | kategori
----+---------------------+-------------
3 | Fisika untuk Pemula | Sains
5 | Dasar Basis Data | Pemrograman
6 | Cerita Kota Lama | Fiksi
8 | Jaringan Komputer | Pemrograman
(4 rows)Tiga tabel tergabung dalam satu query, dan klausa WHERE menyaring peminjaman yang belum kembali, yang kekosongannya dibahas pada topik semantik NULL. Kata INNER boleh ditulis maupun tidak, karena JOIN polos di PostgreSQL berarti INNER JOIN.
USING dan NATURAL#
Ketika kolom penghubung kedua tabel bernama sama, klausa USING meringkas syaratnya sekaligus menyatukan kolom hasilnya menjadi satu kolom. Query peminjaman di atas dapat ditulis dengan JOIN anggota USING (id) bila kolom id-nya memang sama nama, dan hasilnya tidak lagi menampilkan kolom id ganda.
NATURAL JOIN menggabungkan berdasarkan seluruh kolom yang bernama sama tanpa menyebutkannya. Kemudahan itu rapuh, karena penambahan satu kolom sama nama pada kedua tabel mengubah makna join tanpa mengubah query, sehingga USING atau ON eksplisit tetap menjadi pilihan baku.
Kesalahan umum#
Kesalahan pertama adalah lupa syarat ON pada FROM yang memakai koma, yaitu FROM peminjaman, anggota, yang menghasilkan produk kartesian semua pasangan tanpa menyaring apa pun, dan gejalanya jumlah baris yang melonjak. Kesalahan kedua adalah menyebut kolom tanpa awalan ketika kolom itu ada di kedua tabel, misalnya id, yang ditolak server dengan pesan kolom ambigu.
Kesalahan ketiga adalah menaruh syarat penyaringan tabel kanan pada WHERE untuk join yang akan dibahas sebagai outer join, kebiasaan yang diam-diam mengubah makna join-nya. Untuk INNER JOIN keduanya setara, dan pembeda ini dibedah pada topik LEFT JOIN berikutnya.
Pola penyaringan yang menyertai join layak dijaga sejak awal, yaitu menaruh syarat pada ON ketika ia mendefinisikan pasangan dan pada WHERE ketika ia menilai hasil. Kebiasaan ini membuat query yang ditambah tabel tidak berubah maknanya, karena syarat barunya menemukan tempat yang benar tanpa meninjau ulang seluruh query. Pemeriksaan akhirnya sederhana, yaitu membandingkan jumlah baris hasil dengan tabel fakta yang menjadi pusatnya, dan selisih yang tidak dijelaskan selalu berarti satu syarat yang salah tempat.
Ringkasan#
- INNER JOIN hanya mempertahankan pasangan baris yang cocok menurut syarat ON, dan baris tanpa pasangan dibuang.
- Alias pendek menjaga query join tetap terbaca, dengan awalan jelas untuk setiap kolom yang ambigu.
- USING meringkas join ketika kolom penghubungnya sama nama, sedangkan NATURAL JOIN dihindari karena maknanya berubah mengikuti skema.
- Koma pada FROM tanpa syarat adalah produk kartesian tersembunyi yang menyebabkan jumlah baris melonjak.
Referensi#
- Joins Between Tables — PostgreSQL Documentation — Bagian tutorial resmi tentang join.
- Joined Tables — PostgreSQL Documentation — Referensi seluruh jenis join pada dokumentasi resmi.
- SELECT — PostgreSQL Documentation — Referensi klausa FROM dan bentuk-bentuk join-nya.