author-pic

Ferry S

An ISTJ, Type 5, Engineer, Gamer, and Thriller-Movies-Lover
Normalisasi Database
Monday Aug 22nd, 2022 12:15 pm13 mins read
Tips & Tutorial, Programming Principle
Normalisasi Database
Source: Pixabay - Normalization Database

Normalisasi Database adalah proses strukturisasi data dalam rangka mengurangi Data Redundancy dan meningkatkan Data Integrity. Data Redundancy adalah pengulangan data yang sama sehingga beresiko terhadap kesalahan penginputan. Data Integrity adalah data yang yang mudah dimaintain, akurat, dan konsisten. Lawan kata dari Normalisasi adalah Denormalisasi. Data Denormalisasi memiliki redundancy yang tinggi dan integritasnya rendah. Oleh karena itu, kita butuh menormalisasi data untuk mencapai kedua tujuan tersebut.

Kali ini kita akan mencoba melakukan strukturisasi data dari Denormalized menjadi Normalized hingga bentuk normalisasi kelima (5NF). Kita ambil satu contoh data yang Denormalized menggunakan use case data perfilman seperti ini:

Table director

nik name origin phone_no
123 Kimo DKI Jakarta 08555
321 Timo Papua 08333
999 Hanung DKI Jakarta 08111,08222

1NF

Kriteria untuk memenuhi 1NF adalah tidak ada multivalue pada sebuah kolom. Pada table director, kolom phone_no dengan NIK 999 memiliki multivalue karena mempunyai lebih dari satu nomor telepon, sehingga melanggar 1NF. Kekurangan pada data di atas adalah ketika data dengan NIK 999 mengganti salah satu nomornya. Misalkan dari 08222 ke 08221 maka kita harus update kolom tersebut dengan query memasukkan salah satu nomor lama, lalu tambahkan nomor baru di belakangnya secara comma-separated. Ke depannya bakal ribet maintain data seperti ini. Untuk itu kita perlu normalisasi di sini menjadi seperti berikut:

Table director

nik name origin phone_no
123 Kimo DKI Jakarta 08555
321 Timo Papua 08333
999 Hanung DKI Jakarta 08111
999 Hanung DKI Jakarta 08222

Sekarang sudah tidak ada lagi satu kolom yang memiliki multivalue karena datanya sudah dipisah. Sehingga sekarang tabel di atas sudah memenuhi kriteria 1NF. Ketika sutradara dengan NIK 999 mau mengganti nomornya dari 08222 ke 08221 maka cukup update row yang menyimpan value 08222 saja tanpa mengganggu data pada nomor lainnya.

Key Dalam Database

Sebelum masuk ke tingkat normalisasi selanjutnya, kita perlu tahu dulu beberapa “key” dalam database.

Candidate key adalah kolom unik yang menjadi penanda bahwa data tersebut merupakan satu kesatuan data. Candidate key dari tabel di atas adalah nik dan phone, karena itu merupakan kolom unik. Kolom name bukan candidate key karena setiap orang bisa saja memiliki nama yang sama. Begitu juga dengan origin.

Primary Key adalah kolom unik yang menjadi pembeda antar row yang bisa dijadikan referensi dari data tersebut dan tidak boleh null. Candidate Key boleh dijadikan Primary Key. Tapi best practice-nya Candidate Key tidak dianjurkan menjadi Primary Key, karena candidate key bisa saja berubah nilainya. Misalkan pada tabel di atas kolom nik kita jadikan primary key. Kemudian data nik tersebut berelasi dengan tabel lainnya. Lalu ternyata ada typo saat input NIK di awal dan harus diganti. Tentu tidak semudah itu mengganti Primary Key ketika datanya sudah berelasi. Begitu juga dengan kolom phone_no, nilainya memang unik, tapi nomor telepon itu bisa berubah atau berpindah tangan. Oleh karena itu, best practice-nya adalah menggunakan auto-incremented value sebagai Primary Key, sedangkan Candidate Key cukup diberi constraint Unique Key.

Unique Key adalah kolom yang memiliki value yang tidak boleh duplikat. Bedanya dengan Primary Key adalah kita bisa membuat Unique Key nullable atau non-nullable.

Foregin Key adalah kolom yang menjadi penghubung relasi antar tabel sebagai referensi dari satu tabel ke tabel lainnya. Untuk lebih jelasnya nanti ada contohnya di bawah.

2NF

Syarat untuk memenuhi kriteria 2NF adalah sudah memenuhi syarat 1NF dan tidak ada partial functional dependency pada non-candidate key di tabel relasi. Functional Dependency adalah kolom yang dependant pada kolom lainnya. Sebuah kolom disebut dependant jika data dari kolom tersebut bisa diidentifikasi lewat Candidate Key. Partial Functional Dependency adalah ketika ada kolom yang hanya dependant pada salah satu Candidate Key saja. Contohnya pada data di atas Candidate Key-nya adalah nik dan phone_no. Data nik bisa mengidentifikasi nama dan daerah asal sutradara. Tetapi data phone_no tidak bisa mengidentifikasi nama dan daerah asal sutradara, karena bisa saja si sutradara memiliki lebih dari satu nomor telepon, atau nomornya berpindah tangan, atau bahkan mengganti nomor teleponnya. Nama dan asal sutradara tersebut hanya bergantung pada nik saja. Inilah yang disebut partial functional dependency sehingga tidak memenuhi syarat 2NF. Masalahnya adalah akan terdapat pengulangan beberapa row data jika sutradara tersebuk memiliki nomor lebih dari satu sehingga ribet dimaintain. Contohnya ketika sutradara dengan NIK 999 ganti nama atau ganti daerah asal, maka butuh update beberapa row. Oleh karena itu kolom-kolom yang memiliki partial functional dependency tersebut dipisah menjadi tabel sendiri agar memenuhi syarat 2NF seperti berikut:

Table director

id nik name origin
1 123 Kimo DKI Jakarta
2 321 Timo Papua
3 999 Hanung DKI Jakarta

Table phone

id phone_no director_id
1 08111 1
2 08222 1
3 08555 2
4 08333 3

Sekarang tidak ada lagi pengulangan data pada tabel-tabel di atas. Di sini kita juga udah memisahkan primary key-nya menggunakan auto-incremented, sedangkan candidate key menggunakan constraint unique key. Kedua tabel tersebut dihubungkan oleh kolom director_id di tabel phone. Itulah yang disebut foregin key seperti penjelasan sebelumnya. Sekarang ketika mengganti nama sutradara dengan NIK 999 cukup update satu row saja.

3NF

Misalkan tabel director dibuat sebagai berikut:

Table director

id nik name origin_code origin_province
1 123 Kimo JKT DKI Jakarta
2 321 Timo PAP Papua
3 999 Hanung JKT DKI Jakarta

Kriteria yang memenuhi 3NF adalah sudah memenuhi 2NF dan tidak terdapat transitive dependency pada tabel relasi. Transitive Dependency adalah ketika kolom A bergantung pada kolom B dan kolom B bergantung pada kolom C, tetapi kolom C tidak bergantung pada kolom B, maka kolom A secara tidak langsung bergantung ke kolom C pada tabel relasi. Biar ga pusing kita ambil contoh di atas. Tabel di atas sudah memenuhi 2NF karena tidak ada partial functional dependency. Kolom origin_province bergantung ke kolom origin_code dan kolom origin_code bergantung ke kolom nik, sehingga kolom origin_province secara tidak langsung bergantung ke kolom nik, tetapi kolom nik tidak bergantung pada kolom origin_code. Jadi dari NIK, kita bisa tahu sutradara tersebut kode asalnya apa, dan dari kode tersebut kita bisa tahu itu provinsi apa, tapi dari kode tersebut kita tidak bisa tahu itu NIK siapa karena satu provinsi bisa terdapat lebih dari satu sutradara. Inilah yang disebut transitive dependency. Masalah pada transitive dependency pada tabel di atas adalah, jika data pada kolom origin_province, misalkan sebelumnya dengan value "DKI Jakarta" berganti nama menjadi "Jakarta", maka perlu update semua row yang memiliki value "DKI Jakarta". Kalau data sutradara yang berasal dari DKI Jakarta sangat banyak tentu ga efisien. Sekarang kita ubah menjadi 3NF:

Table director

id nik name origin_id
1 123 Kimo 1
2 321 Timo 2
3 999 Hanung 1

Table origin

id origin_code province
1 JKT DKI Jakarta
2 PAP Papua

Tabel di atas sudah memenuhi 3NF. Tidak ada lagi transitive dependency. Ketika melakukan perubahan daerah asal cukup ganti pada tabel origin saja, tabel director hanya mereferensikan datanya lewat foreign key origin_id.

BCNF

BCNF adalah singkatan dari Boyce-Codd Normalization Form. Ini sebenarnya mirip dengan 3NF dan sering disebut 3.5NF. Keduanya memiliki kriteria yang mirip, hanya saja pada BCNF tidak ada functional dependency sama sekali pada tabel yang memiliki lebih dari satu Candidate Key yang saling bergantung untuk menghindari pengulangan data. Tagline-nya adalah "Nothing but the key". Jadi pada tabel relasi, isinya hanya Candidate Key saja. Contohnya pada tabel berikut:

Table movie

id name year
1 Rumah Dara 2010
2 Ratu Ilmu Hitam 2019
3 Sebelum Iblis Menjemput 2018

Table director

id nik name movie_id
1 123 Kimo 1
2 321 Timo 1
3 123 Kimo 2
4 321 Timo 3

Tabel di atas memiliki 2 Candidate Key yaitu nik dan movie_id yang saling bergantung. Tabel di atas memenuhi 3NF karena tidak ada transitive dependency. Tapi melanggar BCNF karena terdapat functional dependency, yaitu name yang hanya bergantung pada nik. Satu sutradara bisa menyutradai lebih dari satu film, dan satu film bisa disutradai lebih dari satu orang. Kimo menyutradai Rumah Dara & Ratu Ilmu Hitam, Timo menyutradai Rumah Dara & Sebelum Iblis Menjemput. Oleh karena itu akan terjadi pengulangan data seperti di atas. Next kita coba terapkan BCNF seperti berikut:

Table movie

id name year
1 Rumah Dara 2010
2 Ratu Ilmu Hitam 2019
3 Sebelum Iblis Menjemput 2018

Table director

id nik name
1 123 Kimo
2 321 Timo
3 999 Hanung

Table movie_director

director_id movie_id
1 1
1 2
2 1
2 3

Kita bikin tabel Composite khusus untuk mapping referensi data director dan movie yang hanya berisi Key tanpa dependency lain. Setelah kita pisah tabel relasinya seperti di atas, tidak ada lagi pengulangan data. Ini cocok untuk many-to-many relationship.

1NF hingga BCNF adalah normalisasi paling dasar pada RDBMS dan paling sering ditemui saat melakukan design schema. Selanjutnya kita lanjut ke part yang lebih ekstreme😎.

4NF

Umumnya, normalisasi yang lebih kuat dari 3NF atau BCNF ditemui pada kasus yang benar-benar kompleks. Kriteria 4NF adalah memenuhi syarat BCNF dan tidak ada multivalued dependency pada tabel relasi. Multivalued Dependency adalah ketika kolom A & B saling bergantung dan kolom B & C juga saling bergantung, sehingga terdapat lebih dari satu dependency pada masing-masing key. Biasanya ditandai dengan lebih dari dua kolom composite key. Langsung saja ke contoh berikut:

Table director

id nik name
1 123 Kimo
2 321 Timo
3 999 Hanung

Table genre

id name
1 Horror
2 Comedy
3 Biography
4 Drama
5 Acion
6 Thriller

Table movie

id name year
1 Rumah Dara 2010
2 Ratu Ilmu Hitam 2019
3 Sebelum Iblis Menjemput 2018
4 Rudy Habibie 2016

Table director_movie_genre

director_id movie_id genre_id
1 1 1
1 1 6
2 1 1
2 1 6
3 4 3
3 4 4

Tabel di atas sudah memenuhi BCNF karena tidak ada functional dependency pada tabel relasi. Tapi tabel di atas relasinya multivalued sehingga melanggar 4NF karena satu sutradara bisa menyutradai lebih dari satu film, satu film bisa disutradai lebih dari satu orang, dan satu film bisa saja memiliki lebih dari satu genre, begitu juga satu genre bisa saja termasuk ke dalam lebih dari satu film. Jadi bisa disimpulkan bahwa, sutradara Kimo menyutradai film Rumah Dara, Rumah Dara adalah film Horror & Thriller, dan Kimo adalah sutradara film genre Horror & Thriller. Masalahnya adalah akan terjadi pengulangan data setiap ada film baru dengan multiple genre atau multiple sutradara. Mulai kompleks kan🤯? Solusinya kita akan pecah tabel director_movie_genre menjadi seperti berikut:

Table movie_director

director_id movie_id
1 1
2 1
3 4

Table movie_genre

movie_id genre_id
1 1
1 6
4 3
4 4

Setelah dipecah, sekarang tidak ada lagi multivalued dependency karena antara movie_director dan movie_genre masing-masing sudah independent. Jadi tiap ada film baru, tinggal tambahin genre dan sutradaranya di masing-masing tabel.

5NF

Kriterianya adalah memenuhi syarat 4NF dan tidak ada Join Dependency pada tabel relasi. Join Dependency adalah ketika kolom A & B saling bergantung, kolom B & C saling bergantung, dan kolom A & C juga saling bergantung. Jadi masing-masing kolom pada tabel relasi saling bergantung. Biar lebih paham bisa dilihat dari skema di bawah:

Table genre

id name
1 Horror
2 Comedy
3 Biography
4 Drama
5 Acion
6 Thriller

Table movie

id name year
1 Rumah Dara 2010
2 Ratu Ilmu Hitam 2019
3 Sebelum Iblis Menjemput 2018
4 Rudy Habibie 2016

Table subgenre

id name
1 Slasher
2 Gore
3 Supernatural
4 Psychological

Table movie_genre

movie_id genre_id
1 1
1 6
4 3
4 4

Table genre_subgenre

genre_id subgenre_id
1 1
1 2
1 3
1 4

Satu film memiliki beberapa genre, satu genre bisa juga termasuk ke dalam beberapa film. Satu genre bisa memiliki beberapa subgenre, satu subgenre juga bisa termasuk ke dalam beberapa genre. Satu film bisa saja termasuk ke beberapa subgenre, dan satu subgenre juga bisa termasuk ke dalam beberapa film. Jadi bisa disimpulkan seperti film A memiliki genre B, dan genre B memiliki subgenre C & D, namun film A tersebut hanya termasuk subgenre C saja. Makin kompleks kan😱. Tabel di atas sudah memenuhi 4NF karena tidak ada multivalued dependency. Tapi melanggar 5NF karena pada data di atas, film Rumah Dara bergenre Horror dan Thriller. Genre Horror memiliki subgenre seperti Slasher, Gore, Supernatural, dan Psychological. Akan tetapi film Rumah dara hanya termasuk ke dalam subgenre Slasher dan Gore saja. Sehingga kita harus memastikan bahwa subgenre dari film Rumah Dara hanya Slasher dan Gore. Maka dari itu kita normalize tabel di atas dengan menambahkan tabel baru seperti berikut:

Tabel movie_subgenre

movie_id subgenre_id
1 1
1 2

Setelah dinormalisasi, sekarang tidak ada lagi redundancy pada skema tersebut. Ini adalah normalisasi terakhir dimana tidak ada redundancy sama sekali.

Verdict

Kita telah melakukan praktek normalisasi data dari denormalized menjadi normalized hingga 5NF. Sebenarnya 6NF juga ada, tapi 5NF sudah merupakan bentuk normal paling akhir dalam rangka mengurangi Data Redundancy. 6NF hanya bentuk penyempurnaannya saja dengan memastikan setiap kolom dari tabel relasi dipecah menjadi composite terkecil. Makanya 5NF sebenarnya udah cukup disebut bentuk paling normal. Oh ya, tidak semua kasus bisa langsung diterapkan Normalisasi yang tinggi, semuanya dilakukan secara bertahap dari 1NF hingga seterusnya tergantung use case. Normalized data cocok untuk menyimpan data transaksi (OLTP) yang membutuhkan integritas yang kuat dengan redundancy yang minimal. Tapi bukan berarti Denormalized Data merupakan skema yang buruk. Denormalized Data lebih cocok untuk menampung data analytics (OLAP) karena biasanya berfokus pada history data yang banyak, bukan untuk transaksi sehingga lebih optimized. Untuk Denormalized Data biasanya lebih cocok menggunakan Column-Based NoSql daripada RDBMS.