Third Normal Form (3NF) — Database

Third Normal Form (3NF) Syarat 3NF: Sudah memenuhi 2NF Tidak ada transitive dependency — kolom non-key tidak boleh bergantung pada kolom non-key lainnya…

Third Normal Form (3NF) adalah aturan normalisasi yang ketiga: kolom non-key gak boleh bergantung pada kolom non-key lain. Setiap kolom harus menjelaskan baris itu sendiri, bukan menjelaskan kolom tetangganya. Bayangin papan menu Kedai Senja yang menulis nama kategori di samping setiap menu: "Kopi Susu, kategori Kopi", "Kopi Susu Aren, kategori Kopi". Nama kategori itu sebenarnya milik kategorinya, bukan milik menunya. Mau ganti nama kategori "Kopi" berarti menghapus dan menulis ulang di samping setiap menu kopi.

🧪 Tebakan cepat: tabel products menyimpan category_id dan category_name. Kamu mengganti nama kategori 1 di baris Kopi Susu Aren saja. Kalau ditanya "kategori 1 namanya apa?", jawabannya satu nama atau dua nama? Tebak dulu, jawabannya ada di bagian "Satu kategori, dua nama".

Tabel yang kita pakai

Setelah 2NF, Kedai Senja punya tabel products dengan primary key satu kolom, id. Partial dependency sudah gak mungkin. Tapi lihat dua kolom terakhirnya:

id name price category_id category_name
1 Kopi Susu 18000 1 Kopi
2 Kopi Susu Aren 22000 1 Kopi
3 Teh Tarik 15000 2 Non-Kopi
4 Roti Bakar 20000 3 Makanan

Rantai dependency: transitive dependency

Ikuti siapa menentukan siapa:

id  ->  category_id  ->  category_name
 1  ->       1       ->  Kopi

Ketergantungan berantai seperti itu disebut transitive dependency: kolom non-key bergantung pada kolom non-key lain, dan baru lewat kolom itu sampai ke primary key. Syarat 3NF ada dua: tabelnya sudah memenuhi 2NF, dan gak ada transitive dependency. products di atas melanggar 3NF karena category_name.

Masalah nomor HP Budi di pelajaran pertama juga sama bentuknya: di tabel orders yang serba-ada, customer_phone bergantung pada pelanggannya, bukan pada id pesanan.

Satu kategori, dua nama

Pemilik kedai mengganti nama kategori dari halaman Kopi Susu Aren:

UPDATE products SET category_name = 'Kopi Nusantara' WHERE id = 2;

SELECT DISTINCT category_id, category_name
FROM products
ORDER BY category_id;
category_id category_name
1 Kopi
1 Kopi Nusantara
2 Non-Kopi
3 Makanan

Kategori 1 sekarang punya dua nama, dan database gak protes. Itu jawaban tebakannya. Ini update anomaly yang sama seperti di 2NF, cuma penyebabnya beda. Saudaranya juga ikut:

Update yang "benar" (WHERE category_id = 1) memang mengubah semua baris, tapi itu 2 baris untuk satu fakta. Dengan 40 menu kopi, 40 baris.

Pindahkan ke tabel sendiri

Kolom yang bergantung pada category_id pindah ke tabel yang primary key-nya category_id itu:

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

CREATE TABLE products (
  id INTEGER PRIMARY KEY,
  name TEXT NOT NULL,
  price INTEGER NOT NULL,
  category_id INTEGER REFERENCES categories(id)  -- foreign key
);

INSERT INTO categories VALUES (1, 'Kopi'), (2, 'Non-Kopi'), (3, 'Makanan');
INSERT INTO products VALUES (1, 'Kopi Susu', 18000, 1), (2, 'Kopi Susu Aren', 22000, 1),
  (3, 'Teh Tarik', 15000, 2), (4, 'Roti Bakar', 20000, 3);
UPDATE categories SET name = 'Kopi Nusantara' WHERE id = 1;

SELECT p.name, c.name AS category_name
FROM products p
JOIN categories c ON c.id = p.category_id
WHERE p.category_id = 1;
name category_name
Kopi Susu Kopi Nusantara
Kopi Susu Aren Kopi Nusantara

Satu baris diubah, semua menu kopi ikut. Roti Bakar dihapus pun, Makanan tetap ada di categories, dan Camilan bisa ditambah kapan saja.

Pola yang sama berlaku di mana pun. Tabel employees (id, name, department_id, department_name): department_name bergantung pada department_id, bukan pada id. Solusinya tabel departments terpisah, dan employees cukup menyimpan department_id sebagai foreign key.

Ringkasan 1NF, 2NF, 3NF

Masalahnya Melanggar Perbaikannya
multi-value: satu sel berisi banyak nilai, atau array dalam kolom ("0812, 0813", ["a","b"]) 1NF satu nilai per sel, sisanya jadi baris di tabel lain
partial dependency: bergantung pada sebagian composite key 2NF pindah ke tabel milik bagian key itu
transitive dependency: non-key bergantung pada non-key 3NF pindah ke tabel milik kolom penentunya
gak ada ketiganya sudah normal

Normal form yang lebih tinggi (BCNF, 4NF, 5NF) ada, tapi kebanyakan aplikasi production cukup sampai 3NF.

⚠️ Jebakan yang sering ketemu

TL;DR: 3NF = sudah 2NF, dan gak ada transitive dependency: kolom non-key gak boleh bergantung pada kolom non-key lain. Di products (id, name, price, category_id, category_name), rantainya id -> category_id -> category_name, jadi mengganti nama kategori di satu baris bikin satu kategori punya dua nama. Perbaikannya: tabel categories sendiri, dan products menyimpan category_id sebagai foreign key (sama seperti employees dengan departments). Urutannya: multi-value atau array dalam kolom melanggar 1NF, partial dependency melanggar 2NF, transitive dependency melanggar 3NF.

Yang akan kamu pelajari