Primary Key & Foreign Key — Database

Primary Key (PK) Primary key adalah kolom (atau kombinasi kolom) yang secara unik mengidentifikasi setiap baris dalam tabel. Jenis Primary Key -- 1…

Primary key adalah kolom (atau gabungan kolom) yang nilainya unik di setiap baris, jadi satu baris bisa ditunjuk dengan pasti. Foreign key adalah kolom yang isinya primary key tabel lain: penunjuk dari baris anak ke baris induknya. Bayangin kartu member Kedai Senja: nomornya unik dan gak pernah berubah walau pemiliknya ganti nama (primary key). Setiap nota cuma mencatat nomor member itu (foreign key). Pertanyaan besarnya muncul waktu seseorang menutup kartu membernya: nota-notanya dibuang, dicoret jadi "tamu", atau penutupan kartunya ditolak?

🧪 Tebakan cepat: Siti punya 2 pesanan. Foreign key orders.customer_id ditulis dengan ON DELETE SET NULL. Kamu menghapus Siti dari customers. Apa yang terjadi pada 2 pesanannya: ikut terhapus, tetap ada tanpa pemilik, atau DELETE-nya ditolak? Tebak dulu, jawabannya ada di bagian "ON DELETE: nasib baris anak".

Primary key: KTP setiap baris

Primary key punya dua sifat: unik dan gak boleh kosong (NULL). Satu tabel cuma punya satu primary key. Coba masukkan pelanggan dengan id yang sudah dipakai Siti:

INSERT INTO customers (id, name) VALUES (2, 'Rina');
-- UNIQUE constraint failed: customers.id

Ada empat cara memilih primary key:

Jenis Contoh Kapan dipakai
Auto-increment id INTEGER PRIMARY KEY (SQLite), AUTO_INCREMENT (MySQL), GENERATED ALWAYS AS IDENTITY (PostgreSQL) pilihan paling umum, angkanya diisi database: 1, 2, 3
UUID '3f2b9c1e-...', dibuat aplikasi atau gen_random_uuid() di PostgreSQL id dibuat di banyak server, atau id gak boleh bisa ditebak dari URL
Natural key kode negara 'ID', 'JP' nilai dari dunia nyata yang dijamin unik dan gak pernah berubah
Composite key PRIMARY KEY (order_id, product_id) di order_items tabel penghubung, seperti di pelajaran 2NF

Pilih nilai yang gak pernah berubah. Email dan nomor HP kelihatannya unik, tapi bisa diganti pemiliknya, dan setiap foreign key yang menunjuknya harus ikut diubah. Karena itu kebanyakan tabel memakai id auto-increment.

Foreign key: penunjuk ke primary key lain

Di pelajaran SQL Relasi Antar Tabel kamu sudah menulis foreign key. Ini tabel orders Kedai Senja, sekarang dengan aturan hapusnya:

PRAGMA foreign_keys = ON;

CREATE TABLE customers (
  id INTEGER PRIMARY KEY,
  name TEXT NOT NULL
);

CREATE TABLE orders (
  id INTEGER PRIMARY KEY,
  customer_id INTEGER REFERENCES customers(id) ON DELETE SET NULL,
  total INTEGER NOT NULL
);

INSERT INTO customers VALUES (1, 'Budi'), (2, 'Siti'), (3, 'Andi');
INSERT INTO orders VALUES (1, 1, 56000), (2, 1, 22000), (3, 2, 33000), (4, 2, 18000), (5, 3, 15000);

INSERT INTO orders (customer_id, total) VALUES (9, 20000);
-- FOREIGN KEY constraint failed

ON DELETE: nasib baris anak

customers adalah tabel induk, orders tabel anak. Ini hasil DELETE FROM customers WHERE id = 2; (Siti, pemilik pesanan 3 dan 4) di SQLite sungguhan, untuk tiap pilihan ON DELETE:

ON DELETE Hasil DELETE Pesanan 3 dan 4
CASCADE berhasil ikut terhapus
SET NULL berhasil tetap ada, customer_id jadi NULL
RESTRICT ditolak: FOREIGN KEY constraint failed tetap, Siti juga tetap
NO ACTION (bawaan kalau gak ditulis) ditolak: FOREIGN KEY constraint failed tetap, Siti juga tetap

Dengan SET NULL, pesanan Siti tetap ada tanpa pemilik. Itu jawaban tebakannya.

Untuk pesanan di toko, CASCADE dari customers hampir selalu salah: menghapus pelanggan ikut menghapus catatan penjualan dan pembukuan. Pilih RESTRICT atau SET NULL.

Di Laravel: foreignId()->constrained()

Migration Laravel menulis foreign key yang sama dengan lebih ringkas:

$table->foreignId('customer_id')->constrained();                          // REFERENCES customers(id)
$table->foreignId('order_id')->constrained()->cascadeOnDelete();           // ON DELETE CASCADE
$table->foreignId('author_id')->nullable()->constrained('users')->nullOnDelete(); // ON DELETE SET NULL
$table->foreignId('department_id')->constrained()->restrictOnDelete();     // ON DELETE RESTRICT

⚠️ Jebakan yang sering ketemu

TL;DR: Primary key = kolom unik dan gak NULL yang menunjuk satu baris dengan pasti, sebaiknya nilai yang gak pernah berubah (auto-increment, UUID, natural key, atau composite key). Foreign key = kolom yang mereferensikan primary key tabel lain, dan database menolak penunjuk ke baris yang gak ada (FOREIGN KEY constraint failed; di SQLite setelah PRAGMA foreign_keys = ON). ON DELETE menentukan nasib anak saat induk dihapus: CASCADE ikut hapus, SET NULL kosongkan, RESTRICT tolak, NO ACTION (bawaan) juga menolak. Di Laravel: $table->foreignId('customer_id')->constrained(), ditambah cascadeOnDelete(), nullOnDelete() atau restrictOnDelete().

Yang akan kamu pelajari