Relasi Antartabel: Primary Key, Foreign Key & Normalisasi
Kenapa data dipecah ke beberapa tabel, relasi satu-ke-banyak dan banyak-ke-banyak, FOREIGN KEY dengan ON DELETE, serta pengantar normalisasi.
Masalah: semua data dalam satu tabel
Bayangkan tabel pesanan seperti ini:
| id | nama_pelanggan | kota | judul_buku |
|---|---|---|---|
| 1 | Budi Santoso | Jakarta | Laskar Pelangi |
| 2 | Budi Santoso | Jakarta | Dilan 1990 |
| 3 | Budi Santo | Jakarta | Bumi Manusia |
Data Budi ditulis berulang. Kalau Budi pindah kota, kamu harus mengubah banyak baris. Salah satu baris salah ketik ("Budi Santo")? Sekarang database mengira itu orang berbeda. Solusinya: pecah menjadi tabel-tabel kecil yang dihubungkan dengan kunci.
Primary key dan foreign key
- Primary key (PK): kolom yang mengidentifikasi satu baris secara unik, seperti KTP. Contoh
pelanggan.id. - Foreign key (FK): kolom di tabel lain yang menyimpan nilai PK tabel pertama, seperti nomor KTP yang dicatat di formulir. Contoh
pesanan.pelanggan_id.
CREATE TABLE pesanan (
id SERIAL PRIMARY KEY,
pelanggan_id INTEGER NOT NULL REFERENCES pelanggan(id),
tanggal DATE NOT NULL,
status VARCHAR(20) NOT NULL
);
REFERENCES pelanggan(id) membuat PostgreSQL menjaga agar pelanggan_id selalu merujuk pelanggan yang benar-benar ada. Pesanan untuk pelanggan nomor 999 yang tidak ada akan ditolak.
Analogi: FK itu seperti nomor anggota di kartu perpustakaan. Petugas tidak mau mencatat pinjaman untuk nomor anggota yang tidak terdaftar.
Jenis relasi
| Jenis | Artinya | Contoh di BukuKita | Cara membuatnya |
|---|---|---|---|
| Satu-ke-banyak (1:N) | Satu baris A punya banyak baris B | Satu pelanggan punya banyak pesanan | FK di sisi "banyak" (pesanan.pelanggan_id) |
| Banyak-ke-banyak (N:N) | Banyak A terkait banyak B | Satu pesanan berisi banyak buku; satu buku ada di banyak pesanan | Tabel penghubung (detail_pesanan) dengan dua FK |
| Satu-ke-satu (1:1) | Satu A tepat satu B | Pelanggan dan profil lengkapnya | FK + UNIQUE |
Peta database BukuKita
kategori (1) ──< (N) buku (1) ──< (N) detail_pesanan (N) >── (1) pesanan (N) >── (1) pelanggan
ON DELETE: apa yang terjadi bila induknya dihapus?
| Opsi | Perilaku |
|---|---|
NO ACTION / RESTRICT (default) | Penghapusan ditolak selama masih ada anak yang merujuk |
ON DELETE CASCADE | Anak ikut terhapus otomatis (hati-hati!) |
ON DELETE SET NULL | Kolom FK pada anak diisi NULL |
buku_id INTEGER REFERENCES buku(id) ON DELETE CASCADE
Normalisasi dalam tiga kalimat
- Satu sel, satu nilai: jangan simpan "Novel, Komik" dalam satu kolom.
- Satu fakta di satu tempat: nama kota pelanggan disimpan sekali di tabel pelanggan, bukan di setiap pesanan.
- Kolom bergantung pada kuncinya: tabel pesanan hanya menyimpan fakta tentang pesanan.
Itulah intisari bentuk normal 1, 2, dan 3. Hasilnya: tidak ada duplikasi, tidak ada data yang saling bertentangan, dan perubahan cukup dilakukan di satu tempat.
Kasus nyata: mengapa pesanan tidak menyimpan nama pelanggan berulang-ulang
Bayangkan tabel pesanan yang menyimpan nama dan alamat pelanggan di setiap baris. Ketika Dewi pindah kota, kamu harus mengubah ratusan baris, dan satu yang terlewat membuat data saling bertentangan. Dengan relasi, alamat Dewi disimpan sekali di tabel pelanggan, dan pesanan hanya mencatat pelanggan_id:
pelanggan (id, nama, kota) 1 ---- < pesanan (id, pelanggan_id, tanggal)
pesanan (id) 1 ---- < detail_pesanan (pesanan_id, buku_id, jumlah)Prinsipnya: satu fakta disimpan di satu tempat. Inilah inti normalisasi.
Kesalahan yang sering terjadi
- Menyimpan beberapa nilai dalam satu sel ("Novel, Komik"). Pisahkan menjadi baris di tabel relasi.
- Tidak membuat
FOREIGN KEYsehingga muncul data yatim (pesanan untuk pelanggan yang tidak ada). - Normalisasi berlebihan sampai query butuh 10 JOIN. Cari keseimbangan sesuai kebutuhan.