KelasIT.com
Database

Memahami Primary Key dan Foreign Key

Pahami cara kerja primary key dan foreign key, perbedaan surrogate dan natural key, aksi ON DELETE, serta dasar normalisasi agar rancangan database konsisten dan bebas data yatim.

Memahami Primary Key dan Foreign Key

Database relasional kuat karena data dipecah ke beberapa tabel yang saling berhubungan. Hubungan itu dijaga oleh dua jenis kunci: primary key dan foreign key. Memahaminya dengan benar menentukan apakah datamu konsisten atau penuh duplikasi dan data yatim.

Primary Key (kunci utama)

Primary key adalah kolom (atau gabungan kolom) yang mengidentifikasi satu baris secara unik. Syaratnya:

  • Unik: tidak boleh ada dua baris dengan nilai yang sama.
  • Tidak boleh NULL.
  • Stabil: sebaiknya tidak pernah berubah.
  • Ringkas: tipe kecil (integer) lebih cepat dibandingkan teks panjang.
CREATE TABLE pelanggan (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    nama VARCHAR(100) NOT NULL,
    email VARCHAR(150) NOT NULL UNIQUE
);

Setiap tabel hanya boleh punya satu primary key. Di InnoDB (mesin penyimpanan bawaan MySQL), primary key juga menentukan urutan fisik data (clustered index), sehingga pencarian berdasarkan PK sangat cepat.

Surrogate key vs natural key

JenisContohKelebihanKekurangan
Surrogateid auto-increment atau UUIDStabil, ringkas, tak berarti bisnisTidak bermakna bagi manusia
NaturalNIK, ISBN, kode produkBermaknaBisa berubah, bisa panjang, aturan bisnis dapat berganti

Praktik umum: pakai surrogate key sebagai PK, dan beri batasan UNIQUE pada natural key (misalnya email atau NIK) agar tidak terduplikasi.

Composite key

PK dari gabungan beberapa kolom, umum pada tabel penghubung:

CREATE TABLE siswa_kelas (
    siswa_id BIGINT UNSIGNED,
    kelas_id BIGINT UNSIGNED,
    PRIMARY KEY (siswa_id, kelas_id)
);

Foreign Key (kunci tamu)

Foreign key adalah kolom yang merujuk ke primary key tabel lain. Ia menjamin bahwa nilai yang dirujuk benar-benar ada.

CREATE TABLE pesanan (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    pelanggan_id BIGINT UNSIGNED NOT NULL,
    total DECIMAL(12,2) NOT NULL,
    FOREIGN KEY (pelanggan_id) REFERENCES pelanggan(id)
);

Dengan aturan ini, database menolak:

  • menambah pesanan untuk pelanggan_id yang tidak ada;
  • menghapus pelanggan yang masih memiliki pesanan (sesuai aksi yang kamu pilih).

Tipe data kolom FK harus sama persis dengan PK yang dirujuk (misalnya sama-sama BIGINT UNSIGNED), kalau tidak pembuatan constraint akan gagal.

Aksi referensial: ON DELETE dan ON UPDATE

Menentukan apa yang terjadi pada baris "anak" saat baris "induk" dihapus:

AksiPerilakuContoh penggunaan
RESTRICT / NO ACTIONTolak penghapusan jika masih ada anakPelanggan yang punya pesanan
CASCADEAnak ikut terhapusItem pesanan saat pesanan dihapus
SET NULLKolom FK anak diisi NULL (kolom harus boleh NULL)Kategori dihapus, produk tetap ada
SET DEFAULTDiisi nilai bawaanJarang dipakai
FOREIGN KEY (pesanan_id) REFERENCES pesanan(id) ON DELETE CASCADE
Hati-hati dengan CASCADE: satu perintah hapus bisa menghilangkan banyak data sekaligus. Gunakan hanya untuk data yang memang tidak bermakna tanpa induknya.

Jenis relasi

  1. Satu-ke-banyak (1:N): satu pelanggan punya banyak pesanan. FK ada di sisi "banyak".
  2. Satu-ke-satu (1:1): pengguna dan profilnya. FK dengan batasan UNIQUE.
  3. Banyak-ke-banyak (N:M): siswa dan kelas. Butuh tabel penghubung (pivot) berisi dua FK.

Dasar normalisasi

Normalisasi adalah proses merapikan tabel untuk mengurangi duplikasi:

  • 1NF: setiap sel berisi satu nilai (jangan simpan "Matematika, Fisika" dalam satu kolom).
  • 2NF: semua kolom bergantung pada seluruh PK (relevan untuk composite key).
  • 3NF: kolom non-kunci tidak bergantung pada kolom non-kunci lain. Contoh: jangan menyimpan nama_kota dan kode_pos bersama di tabel pelanggan jika kode pos menentukan kota; pindahkan ke tabel tersendiri.

Contoh buruk (duplikasi):

pesanan_idnama_pelangganalamat_pelanggan
1SariJl. Merdeka 1
2SariJl. Merdeka 1

Jika Sari pindah rumah, kamu harus mengubah banyak baris dan mudah terlewat. Solusinya: simpan pelanggan di tabel sendiri dan pesanan hanya menyimpan pelanggan_id.

Praktik terbaik

  • Selalu deklarasikan foreign key di database, jangan hanya mengandalkan aplikasi.
  • Beri nama konsisten: pelanggan_id merujuk ke pelanggan.id.
  • Foreign key otomatis mendapat indeks di InnoDB; ini mempercepat JOIN.
  • Pertimbangkan soft delete (kolom deleted_at) pada data bisnis penting daripada menghapus permanen.
  • Di Laravel: $table->foreignId('pelanggan_id')->constrained()->cascadeOnDelete();

Contoh perancangan: sistem perpustakaan

Entitas: Anggota, Buku, Peminjaman. Seorang anggota bisa meminjam banyak buku dan satu buku bisa dipinjam banyak anggota dari waktu ke waktu, jadi relasinya banyak-ke-banyak. Karena itu peminjaman menjadi tabel penghubung yang sekaligus menyimpan data tambahan.

CREATE TABLE anggota (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    nama VARCHAR(100) NOT NULL,
    nomor_anggota VARCHAR(20) NOT NULL UNIQUE
);

CREATE TABLE buku (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    judul VARCHAR(200) NOT NULL,
    isbn VARCHAR(20) UNIQUE
);

CREATE TABLE peminjaman (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    anggota_id BIGINT UNSIGNED NOT NULL,
    buku_id BIGINT UNSIGNED NOT NULL,
    dipinjam_pada DATE NOT NULL,
    dikembalikan_pada DATE NULL,
    FOREIGN KEY (anggota_id) REFERENCES anggota(id) ON DELETE RESTRICT,
    FOREIGN KEY (buku_id) REFERENCES buku(id) ON DELETE RESTRICT
);

Keputusan desain yang patut diperhatikan:

  • nomor_anggota dan isbn adalah natural key yang diberi UNIQUE, tetapi PK tetap id surrogate.
  • ON DELETE RESTRICT mencegah penghapusan anggota atau buku yang masih punya riwayat peminjaman.
  • dikembalikan_pada bernilai NULL selama buku belum dikembalikan; query "buku yang sedang dipinjam" cukup WHERE dikembalikan_pada IS NULL.

Apa yang terjadi saat constraint dilanggar?

-- Anggota 999 tidak ada
INSERT INTO peminjaman (anggota_id, buku_id, dipinjam_pada) VALUES (999, 1, CURDATE());
-- ERROR 1452: Cannot add or update a child row: a foreign key constraint fails

-- Menghapus anggota yang punya riwayat
DELETE FROM anggota WHERE id = 1;
-- ERROR 1451: Cannot delete or update a parent row: a foreign key constraint fails

Error ini bukan gangguan; justru itulah perlindungan yang mencegah data yatim.

Foreign key dan performa

  • Foreign key butuh index pada kolom FK; InnoDB membuatnya otomatis jika belum ada.
  • Pengecekan FK menambah sedikit biaya pada INSERT/UPDATE/DELETE, tetapi manfaat integritasnya jauh lebih besar untuk hampir semua aplikasi.
  • Pada sistem yang sangat besar (sharding), sebagian tim melepas FK dan menjaga integritas di aplikasi. Itu keputusan lanjutan yang punya konsekuensi; bagi pemula, gunakan FK.

Pertanyaan untuk menguji pemahamanmu

  1. Mengapa kolom email yang UNIQUE tidak dijadikan primary key?
  2. Apa yang terjadi jika tipe anggota_id di peminjaman adalah INT sedangkan anggota.id adalah BIGINT UNSIGNED?
  3. Kapan ON DELETE CASCADE tepat, dan kapan berbahaya?
  4. Bagaimana merancang relasi satu produk punya banyak kategori dan satu kategori berisi banyak produk?

Kesimpulan

Primary key menjawab "baris mana ini?", foreign key menjawab "baris ini milik siapa?". Gunakan surrogate key yang ringkas, deklarasikan foreign key beserta aksi hapus yang tepat, dan terapkan normalisasi dasar. Hasilnya adalah database yang konsisten dan mudah dikembangkan.

H
Hifzon

Menulis tentang pemrograman, database, AI, dan karier di dunia IT supaya belajar terasa lebih mudah dan menyenangkan.

Komentar (0)

Komentar hanya untuk member. Masuk atau daftar gratis untuk ikut berdiskusi.

Mau belajar lebih dalam?

Course online dengan latihan coding interaktif dan sertifikat.

Lihat Course →

Gabung komunitas

Tanya jawab, info event, dan teman belajar di grup WhatsApp.

Gabung Grup