Kardinalitas adalah jawaban untuk pertanyaan "satu baris di tabel A boleh terhubung dengan berapa baris di tabel B, dan sebaliknya?". Jawabannya cuma tiga kemungkinan, dan kamu sudah mengenal ketiganya dari hidup sehari-hari: satu orang punya satu KTP (1:1), satu ibu bisa punya banyak anak tapi tiap anak punya satu ibu kandung (1:N), dan satu siswa ikut banyak ekskul sementara tiap ekskul punya banyak siswa (M:N). Kardinalitas menentukan di mana foreign key ditaruh, jadi salah menebaknya berarti salah membuat tabel.
🧪 Tebakan cepat: pesanan #1 Kedai Senja berisi Kopi Susu dan Roti Bakar, dan Kopi Susu juga ada di pesanan #3. Kolom penunjuknya ditaruh di tabel
orders, di tabelproducts, atau di tempat lain? Tebak dulu, jawabannya ada di bagian "Many-to-many: butuh tabel perantara".
Satu pertanyaan, dua arah
Kardinalitas selalu ditanyakan dari dua arah, dengan contoh data yang nyata:
| Pasangan | Satu A punya berapa B? | Satu B milik berapa A? | Kardinalitas |
|---|---|---|---|
| customers dan profiles | satu (alamat dan tanggal lahir) | satu | 1:1 one-to-one |
| customers dan orders | banyak (Budi pesan 2 kali) | satu (tiap pesanan punya 1 pemilik) | 1:N one-to-many |
| orders dan products | banyak (Kopi Susu dan Roti Bakar) | banyak (Kopi Susu ada di #1 dan #3) | M:N many-to-many |
Pasangan lain yang sering ketemu: user dan profile (1:1), user dan post di blog (1:N), order dan baris item-nya (1:N), student dan course (M:N), tag dan article (M:N). Mahasiswa dan mata kuliah juga M:N: satu mahasiswa mengambil banyak mata kuliah, satu mata kuliah diambil banyak mahasiswa. M:N kadang ditulis N:M atau N:N, artinya sama.
One-to-one (1:1)
Satu customer punya paling banyak satu profile. Foreign key customer_id ditaruh di profiles, ditambah UNIQUE supaya gak ada profile kedua:
CREATE TABLE profiles (
id INTEGER PRIMARY KEY,
customer_id INTEGER NOT NULL UNIQUE REFERENCES customers(id),
address TEXT NOT NULL
);
INSERT INTO profiles (id, customer_id, address) VALUES (1, 1, 'Jl. Melati 5');
INSERT INTO profiles (id, customer_id, address) VALUES (3, 1, 'Jl. Mawar 9');
-- Error: UNIQUE constraint failed: profiles.customer_id
UNIQUEadalah yang membuat ini 1:1. Tanpa dia, Budi bisa punya dua profile dan aplikasi gak tahu mana yang benar.- Kenapa gak digabung saja ke
customers? Biasanya karena datanya opsional (belum semua orang mengisi), jarang dibaca, atau sensitif dan perlu dijaga terpisah.
One-to-many (1:N)
Satu customer punya banyak orders, tiap order milik satu customer. Foreign key ditaruh di sisi banyak, yaitu orders.customer_id, tanpa UNIQUE:
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
customer_id INTEGER NOT NULL REFERENCES customers(id),
ordered_at TEXT NOT NULL
);
Budi pesan dua kali, jadi dua baris orders sama-sama berisi customer_id = 1, dan itu sah. Pesanan #1 gak bisa sekaligus milik Siti: sel customer_id-nya cuma muat satu angka, dan baris kedua dengan id = 1 ditolak (UNIQUE constraint failed: orders.id). Kalau UNIQUE dari contoh 1:1 tadi ikut ditempel di sini, pesanan kedua Budi yang ditolak: modelnya terlalu ketat.
Many-to-many: butuh tabel perantara
Orders dan products gak bisa dihubungkan dengan satu foreign key biasa:
product_iddiorders? Satu sel cuma muat satu produk, padahal pesanan #1 berisi dua. Menulis daftar1,4di satu sel melanggar aturan pelajaran berikutnya.order_iddiproducts? Kopi Susu cuma bisa tercatat di satu pesanan. Memasukkannya ke pesanan #3 denganUPDATE products SET order_id = 3 WHERE id = 1;diam-diam memindahkannya dari pesanan #1, tanpa error.
Jadi jawabannya: di tempat lain, yaitu tabel baru di tengah. Itu jawaban tebakannya. Tabel ini disebut tabel perantara (junction table, atau pivot table di Laravel), dan isinya pasangan foreign key dari kedua sisi:
CREATE TABLE order_items (
order_id INTEGER NOT NULL REFERENCES orders(id),
product_id INTEGER NOT NULL REFERENCES products(id),
qty INTEGER NOT NULL,
PRIMARY KEY (order_id, product_id)
);
INSERT INTO order_items (order_id, product_id, qty) VALUES (1, 1, 2); -- #1: 2 Kopi Susu
INSERT INTO order_items (order_id, product_id, qty) VALUES (1, 4, 1); -- #1: 1 Roti Bakar
INSERT INTO order_items (order_id, product_id, qty) VALUES (3, 1, 1); -- #3: 1 Kopi Susu
- Satu baris
order_itemsadalah satu produk di satu pesanan. M:N dipecah jadi dua relasi 1:N: orders ke order_items, dan products ke order_items. PRIMARY KEY (order_id, product_id): pasangan yang sama gak boleh dua kali. Menambah Kopi Susu lagi ke pesanan #1 denganINSERTditolak (UNIQUE constraint failed: order_items.order_id, order_items.product_id); yang benar menaikkanqty.- Tabel perantara boleh punya kolom sendiri seperti
qty: data yang milik pasangannya, bukan milik pesanan atau produknya saja.
Di mana foreign key-nya
| Kardinalitas | Foreign key | Penjaga |
|---|---|---|
| 1:1 | di salah satu tabel, biasanya yang opsional (profiles.customer_id) |
UNIQUE |
| 1:N | di sisi banyak (orders.customer_id) |
foreign key biasa |
| M:N | di tabel perantara, dua-duanya (order_items) |
PRIMARY KEY gabungan |
📌 Relasi ke diri sendiri
Kadang tabel menunjuk dirinya sendiri, disebut self-referencing. Contohnya karyawan dan atasannya: kolom manager_id di employees menunjuk employees.id. Kardinalitasnya tetap 1:N (satu atasan, banyak bawahan), cuma kedua sisinya tabel yang sama.
⚠️ Jebakan yang sering ketemu
- Terlalu ketat.
UNIQUEdiorders.customer_idmembuat 1:N jadi 1:1, dan pesanan kedua Budi ditolak. - Terlalu longgar. Lupa
UNIQUEdiprofiles.customer_id, dan Budi bisa punya dua profile. - M:N dengan foreign key biasa.
UPDATEdiproducts.order_idmemindahkan produk antar pesanan tanpa error. Datanya hilang diam-diam. - Salah tulis jawaban di latihan. Kembalikan
"1:1","1:N"atau"M:N"persis: huruf besar, titik dua, tanpa spasi. Urutan pasangannya seperti di daftar ("user", "profile"), dan pasangan yang gak ada di daftar mengembalikan"unknown", jangan ditebak.
TL;DR: Kardinalitas menjawab "satu A terhubung dengan berapa B, dan sebaliknya", ditanyakan dua arah dengan data nyata. 1:1 (customer dan profile, user dan profile): foreign key plus UNIQUE. 1:N (customer dan orders, user dan post, order dan item-nya): foreign key di sisi banyak. M:N (orders dan products, student dan course, tag dan article): tabel perantara seperti order_items dengan dua foreign key dan primary key gabungan; foreign key biasa saja gak cukup.