Relasi antar tabel artinya satu tabel menunjuk baris di tabel lain lewat sebuah kolom berisi angka id. Mirip nota belanja di minimarket: nota cuma mencatat "member nomor 1", bukan nama, alamat dan nomor HP-mu. Kalau kasir butuh namamu, dia cari nomor 1 di daftar member. Data member disimpan sekali, nota cukup menunjuk ke sana.
🧪 Tebakan cepat: tabel pesanan terhubung ke tabel pelanggan, dan pelanggannya cuma nomor 1 sampai 3. Kamu menyimpan pesanan atas nama pelanggan nomor 9. Apa yang terjadi: tersimpan, ditolak, atau pelanggan nomor 9 dibuat otomatis? Tebak dulu, jawabannya ada di bagian "Foreign key menjaga hubungannya".
Kenapa dipisah jadi dua tabel
Bayangkan semua pesanan disimpan di satu tabel, lengkap dengan nama pembelinya:
| id | customer_name | product |
|---|---|---|
| 1 | Budi | Laptop |
| 2 | Budi | Mouse |
| 3 | Siti | Monitor |
Kelihatannya rapi, tapi ada tiga masalah. Nama "Budi" ditulis dua kali, jadi kalau Budi ganti nama, kamu harus mengubah setiap barisnya. Dua pelanggan yang namanya sama-sama Budi gak bisa dibedakan. Dan Andi, yang sudah daftar tapi belum pernah pesan, gak punya tempat sama sekali.
Solusinya: pelanggan punya tabel sendiri, dan pesanan cuma menyimpan nomornya.
customers
| id | name |
|---|---|
| 1 | Budi |
| 2 | Siti |
| 3 | Andi |
orders
| id | customer_id | product |
|---|---|---|
| 1 | 1 | Laptop |
| 2 | 1 | Mouse |
| 3 | 2 | Monitor |
| 4 | NULL | Flashdisk |
customer_id 1 artinya "pesanan ini milik baris id 1 di customers", yaitu Budi. Flashdisk dibeli pembeli tamu yang gak punya akun, jadi customer_id-nya NULL: gak menunjuk siapa pun. Data ini dipakai terus sampai pelajaran JOIN dan subquery.
Tiga istilah yang perlu kamu pegang:
- Primary key: kolom yang nilainya unik di setiap baris, di sini
id. Ini "nomor member"-nya. - Foreign key: kolom di satu tabel yang isinya primary key tabel lain, di sini
orders.customer_id. - Tabel induk (parent) adalah yang ditunjuk (
customers). Tabel anak (child) adalah yang menunjuk (orders).
Menulis foreign key
CREATE TABLE customers (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL
);
CREATE TABLE orders (
id INTEGER PRIMARY KEY AUTOINCREMENT,
customer_id INTEGER, -- id pelanggan pemilik pesanan
product TEXT NOT NULL,
amount REAL,
FOREIGN KEY (customer_id) REFERENCES customers(id)
);
customersdibuat duluan, karena dia yang ditunjuk.customer_id INTEGER: tipenya sama dengancustomers.id. Namanya mengikuti kebiasaan umum: nama tabel bentuk tunggal +_id.FOREIGN KEY (customer_id): kolom di tabel ini yang jadi penunjuk.REFERENCES customers(id): dia menunjuk ke kolomiddi tabelcustomers.- Bentuk pendek di baris kolomnya juga boleh:
customer_id INTEGER REFERENCES customers(id). Pilih salah satu, jangan dua-duanya. - Kalau setiap pesanan wajib punya pemilik, tambahkan
NOT NULL:customer_id INTEGER NOT NULL. Di contoh iniNOT NULLgak dipakai, supaya pesanan tamu seperti Flashdisk boleh kosong.
Foreign key menjaga hubungannya
Dengan foreign key, database ikut menjaga supaya setiap customer_id benar-benar menunjuk ke pelanggan yang ada. Ini yang dijawab SQLite di Lab:
| Perintah | Hasil |
|---|---|
INSERT INTO orders (customer_id, product, amount) VALUES (2, 'Keyboard', 500000); |
masuk, menunjuk Siti |
INSERT INTO orders (customer_id, product, amount) VALUES (9, 'Headset', 750000); |
ditolak: FOREIGN KEY constraint failed |
DELETE FROM customers WHERE id = 1; |
ditolak juga: Budi masih punya pesanan |
INSERT INTO orders (customer_id, product, amount) VALUES (NULL, 'Kabel', 25000); |
masuk: NULL gak dicek |
Pesanan untuk pelanggan nomor 9 ditolak, dan gak ada pelanggan yang dibuat otomatis. Itu jawaban tebakannya. Tanpa foreign key, baris itu masuk dan jadi baris "yatim": menunjuk pelanggan yang gak ada.
Satu hal khusus SQLite (dipakai di Lab, Playground dan latihan): penjaga ini baru aktif setelah perintah PRAGMA foreign_keys = ON;. Tanpa itu, foreign key cuma dicatat di skema. MySQL (InnoDB) dan PostgreSQL langsung menjaganya.
Jenis relasi
| Relasi | Contoh | Foreign key-nya di mana |
|---|---|---|
| One-to-many | 1 pelanggan punya banyak pesanan | di tabel sisi "banyak": orders.customer_id |
| One-to-one | 1 user punya 1 profil | di profiles.user_id, ditambah UNIQUE |
| Many-to-many | siswa ikut banyak ekskul, ekskul punya banyak siswa | di tabel perantara siswa_ekskul dengan dua foreign key |
One-to-many paling sering ketemu. Contoh lain: 1 guru mengajar banyak siswa, jadi tabel siswa punya kolom guru_id. Kebalikannya (banyak pesanan milik 1 pelanggan) kadang disebut many-to-one, tapi itu relasi yang sama dilihat dari sisi lain. Tabel perantara untuk many-to-many dibahas di pelajaran Desain Skema.
⚠️ Jebakan yang sering ketemu
- Menyimpan nama, bukan id.
customer_name TEXTdiordersbikin nama tersalin di banyak baris, dan dua orang bernama sama tercampur. - Bikin kolom
customer_idtanpaFOREIGN KEY. Angkanya tersimpan, tapi database gak tahu angka itu menunjuk ke mana, jadi pesanan untuk pelanggan nomor 9 lolos. Latihan mengecek hubungan ini, bukan sekadar nama kolomnya. - Menulis foreign key dua kali.
REFERENCESdi baris kolom ditambahFOREIGN KEY (...)di akhir tabel bikin dua foreign key yang sama. Cukup satu. - Salah nama tabel di
REFERENCES.REFERENCES customer(id)(tanpa s) menunjuk tabel yang gak ada.
TL;DR: Simpan setiap benda di tabelnya sendiri, lalu hubungkan dengan foreign key: kolom di tabel anak (orders.customer_id) yang isinya primary key tabel induk (customers.id). Tulis FOREIGN KEY (customer_id) REFERENCES customers(id) sekali. Database lalu menolak pesanan untuk pelanggan yang gak ada (FOREIGN KEY constraint failed; di SQLite setelah PRAGMA foreign_keys = ON). NULL gak menunjuk siapa pun dan lolos, kecuali kolomnya NOT NULL. Relasinya bisa one-to-many (foreign key di sisi banyak), one-to-one, atau many-to-many (lewat tabel perantara).