What is Database Normalization?

Pawut Jingjit
5 min readFeb 20, 2023

--

ภาพสวยๆจาก https://corporatefinanceinstitute.com/resources/data-science/database/

What is Database Normalization?

The process of structuring a relational database in accordance with a series of so-called normal forms in order to reduce data redundancy and improve data integrity

ขั้นตอนในการจัดการ relational database ให้เป็นรูป normal forms เพื่อลด Data redundancy(การซ้ำซ้อนกันของ Data) และเพิ่ม Data integrity(ความสมบูรณ์ของ Data)

Player Table สีแดงเป็น PKey นะ

เมื่อพิจารณา Player Table จะพบว่า Table ข้างต้น จะมีปัญหากับ Column PlayerItem อย่างมาก

  • Red ได้ Master Ball เพิ่มมาหนึ่งลูก จะอัพเดท PlayerItem ได้อย่างไร ตัด String ยากๆเลยดีไหม?
  • อยากจะนับว่า Green มี Item ในกระเป๋ากี่ชิ้น จะนับได้อย่างไร? ตารางนี้ Group มา Count ไม่ได้นะ
  • วันหนึ่ง Developer ประกาศว่า Ultra Ball ถูกเปลี่ยนชื่อเป็น Ultimate Ball เราจะ Update ชื่อใน PlayerItem อย่างใด?

การแก้ปัญหาดังกล่าว โดยการลดรูป Database ให้เป็น “Normal Form” นั้น คือความหมายของ “Database Normalization”

First Normal Form (1NF)

First Normal Form (1NF) sets the basic rules to organize the data in a database. A database is said to be in first normal form if it satisfies the following conditions:

Rule 1 (Atomic Values) — Every column of a table should contain only atomic values. An atomic value is a value that cannot be divided further.

Rule 2 (No Repeating Groups) — There are no repeating groups of data. This means a table should not contain repeating columns.

Player Table เดิมๆจากต้นบทความ
  • PlayerItem นั้นมีค่าที่สามารถแบ่งออกได้ (Divided) นั้นย่อมไม่ใช่ Atomic Values

การที่ 1 Column มีได้เพียง 1 ค่าเท่านั้น เรียกว่า Atomic Values

เราจึงออกแบบตารางใหม่ ให้เป็น Atomic Values ทั้งหมด ดังภาพ

ด้วยการออกแบบตารางใหม่ของเรา ทุก Column เป็น Atomic Value อย่างแน่นอน ซึ่งแน่นอนว่าผ่าน Rule 1 แต่จะเกิดปัญหาใหม่ขึ้นมา
  • สังเกตว่า Table ใหม่ จะมี Item ซ้ำกันหลาย Column ถ้าอนาคตมี Item เพิ่มขึ้นมา Column จะเพิ่มขึ้นเช่นกัน
  • ค่าส่วนใหญ่ในตาราง Item มีค่า 0 ทำให้สิ้นเปลืองเนื้อที่ในการเก็บ

การที่มีหลาย Column ที่มีความหมายเดียวกัน แต่เก็บคนละค่าเช่นนี้ เรียกว่า Repeating Groups

เราสามารถใช้ความรู้ในด้าน Relational Database มาแก้ไขปัญหาได้

แยกส่วนที่เป็น Repeat Group มาเป็น Table ใหม่ โดยมีความสัมพันธ์แบบ One To Many ได้ตารางใหม่เป็น Item Table
  • Red ได้ MasterBall ขึ้นมา 1 ลูก สามารถเพิ่ม Rows ใหม่ใน Item Table ได้เลย
  • จะนับจำนวน Item ของ Player ก็สามารถทำได้ง่ายผ่าน GROUP BY ใน Item Table

หลังจากกำจัด Repeating Group ไปแล้ว DataBase ของเพื่อนๆจะเป็น First Normal Form (1NF)

อนึ่ง ไม่ค่อยมีคนใช้ ItemName เป็น P-Key เท่าไหร่ เนื่องจากเรามีโอกาสที่จะเปลี่ยนชื่อ Item ได้ แล้วยังยากต่อการ Generate ด้วย เราสามารถสร้าง ItemDesc ขึ้นมา โดยมี Relation เป็น ItemTable ManyToOne ItemDesc ได้
  • ถ้าวันหนึ่ง Developer เปลี่ยนชื่อ Ultra Ball เป็น Ultimate Ball , Developer สามารถเปลี่ยนที่ Item Desc Table ที่ ItemID 4 ได้ทันที

Second Normal Form (2NF)

The Second Normal Form states that it should meet all the rules for 1NF and there must be no partial dependences of any of the columns on the primary key

วันหนึ่ง ทาง Developer นำระบบ Rank เข้ามา แต่ดันเอา Player Rank ไปผูกกับ Item Table แต่การมี Column PlayerRank ทำให้เกิดปัญหาใหม่
  • ถ้า Player 0 เกิดถูกลดขั้นเป็น Junior เราต้องแก้ Player Rank ถึง 2 Rows
  • ถ้า Player 0 เช่นเดิม มี Item 20 ชนิด เราจำเป็นต้องแก้ Player Rank ถึง 20 Rows

เมื่อเราลองคิดให้ดีแล้ว PlayerRank นั้น “ผูก” กับ PlayerID เพียงอย่างเดียว ไม่ “ผูก”กับ PlayerItem เลย

การที่ “บาง” primary key นั้นมี dependences กับ columns ใดๆ นั้นเรียกว่า เกิด partial dependences

แยก Partial Dependencies เป็นตารางใหม่ (Player Rank) โดยมีความสัมพันธ์แบบ Many To One , จุดสังเกตของ 2nd Form คือ จำเป็นต้องใช้ ”ทุก” PKey ในการ “ผูก” กับ Column ใดๆ
  • หลังจากนี้ ถ้าเราจะเปลี่ยนแปลง Player Rank ของ Player ใดๆ สามารถแก้ที่ PlayerRankTable เพียง 1 Rows ได้ทันที

หลังจากกำจัด Partial Dependences ไปแล้ว DataBase ของเพื่อนๆจะเป็น Second Normal Form

Third Normal Form (3NF)

A table is in a third normal form when the following conditions are met −

- It is in second normal form.

- All nonprimary fields are dependent on the primary key.

วันต่อมา Developer คิดว่า ระบบ Rank ไม่ตอบโจทย์อีกต่อไป จึงมีระบบ Level ขึ้นมาด้วย
โดย Game Design บอกมาว่า Level นั้นสัมพันธ์กับ Rank ตามตารางนี้นะ
  • ถ้า Player Level เพิ่มขึ้นจาก 6 ไป 7 เราต้อง Update ทั้ง PlayerRank และ PlayerLevel
เพิ่ม World Pro ใน Business
  • วันหนึ่ง Game Design ต้องการเพิ่ม Rank ใหม่ World Pro สำหรับ Level 10 ขึ้นมา เราจำเป็นต้อง Update ทุก Player ที่ Level 10 เป็น World Pro

เมื่อเราลองคิดดูให้ดี จะพบว่า PlayerRank นั้น “ผูก” กับ Player Level เสมอ ถ้าเราทราบ PlayerLevel จะทราบ PlayerRank

การที่ “บาง” Column “ที่ไม่ใช่ primary key” นั้นมี Dependences กับ Columns ใดๆ นั้นเรียกว่า เกิด Transitive Dependences

แยก Transitive Dependencies เป็นตารางใหม่ โดยมี PKey เป็น Column ที่เกิด TD ของตารางเดิม
  • ถ้า Player Level เพิ่มขึ้นจาก 6 ไป 7 เราจะ Update Player Level เพียง 1 Column
  • วันหนึ่ง Game Design ต้องการเพิ่ม Rank ใหม่ World Pro สำหรับ Level 10 ขึ้นมา เราจะอัพเดทเพียงเพิ่ม Rows ในตาราง PlayerLevel-PlayerRank

หลังจากกำจัด Transitive Dependences ไปแล้ว DataBase ของเพื่อนๆจะเป็น Third Normal Form

Boyce-Codd Normal Form (BCNF)

BCNF is the advance version of 3NF. It is stricter than 3NF.

A table is in BCNF if every functional dependency X → Y, X is the super key of the table.

ตาราง 3NF Visual Novel Ranking ที่มี ReleaseYear,Ranking เป็น PKey
  • Visual Novel Ranking เป็นตาราง 3NF เพราะไม่มีทั้ง PD และ TD
  • ต่อมาวันหนึ่ง มีคนมาแจ้งว่า White Album จริงๆออกปี 2010 ตังหาก เราจำเป็นต้องแก้ทั้ง ReleaseYear และ ReleaseYearMonth
เมื่อลองพิจารณา Dependency ดูแล้ว ได้ตามภาพ เห็นได้ว่า สำหรับ functional dependency X → Y โดย X = ReleaseYearMonth และ Y = ReleaseYear แล้ว X ไม่ใช่ super-key (Y เป็น super-key) การมีความสัมพันธ์เช่นนี้ ทำให้ Table นี้ไม่ใช่ Boyce-Codd Normal Form
ในเมื่อ ReleaseYearMonth มี Dependency กับ ReleaseYear เราก็แค่ทำให้มันไม่มีซะ ด้วยการนำส่วนที่ซ้ำซ้อนกันออก
  • ถ้าเราจำเป็นต้องแก้ ReleaseYear เราก็แก้แค่ ReleaseYear ไม่ต้องยุ่งกับ ReleaseMonthอีก
หรือจะง่ายกว่านั้น สลับ ReleaseYearMonth เป็น PKey เลย จากนั้นจะเกิด Partial Dependencies ระหว่าง ReleaseYearMonth และ ReleaseYear เราก็แค่นำ ReleaseYear ออก
ไม่เกี่ยวกับเนื้อหาสาระแต่อย่างใด คือ White album 2 บทนำออกปี 2010 แต่บทที่ทุกคนจดจำคือ ~closing chapter~ ออกปี 2011

~closing chapters~

เนื่องจากช่วงนี้ ผู้เขียนได้มีโอกาสได้ Implement Backend แล้วรู้สึกว่า ตัวเองก็ออกแบบตารางได้ดีนั่นละ(อวยตัวเองหน่อย LOL) แต่ถ้ามีคนถามขอคำอธิบายจะตอบได้ไม่ดีเลย ซึ่งคิดว่าเพื่อนๆก็น่าจะเป็นเช่นเดียวกัน จึงถือว่าโอกาสนี้เขียนบทความนี้มาแชร์ความรู้กับเพื่อนๆเลยครับ

เนื่องจากเป็นอธิบายล้วนๆเลย จึงแทบจะเป็นบทความที่ยากที่สุดที่ผู้เขียนได้ลองเขียนเลย (ปกติแล้วก็อปโค๊ตแปะๆเอา TT) ก็ถ้าอ่านไม่รู้เรื่องยังไง เพิ่มเติมส่วนไหนแล้วเข้าใจได้มากขึ้น รวมไปถึงตรงไหนผิดพลาด ก็สามารถมาพูดคุยหรือสอนมวยกันได้เลยนะครับ

ที่อยากจะเตือนเพื่อนๆไว้คือ จริงๆแล้ว การ Normalize นั้นมีถึง 5 ขั้นด้วยกัน แต่ปกติทำถึง 3rd Form ก็น่าจะใช้งานได้ดีมากๆแล้ว ประเด็นคือ บางครั้งเราอาจจะยอมไม่ Normalize เพื่อ Performance ได้เหมือนกัน

ที่เจอบ่อยๆคือ 3rd ในฟิลล์ที่มันไม่น่าจะมีการเพิ่ม-เปลี่ยนแปลง ค่า (คิดสภาพ จังหวัด-รหัสไปรษณีย์) Case นี้ เราอาจจะเก็บข้อมูลลดลง แต่ Join นั้นมีราคาที่แพงมาก (มันรันช้า) บางครั้งอาจจะยอมเก็บข้อมูลซ้ำซ้อน เพื่อที่จะได้ไม่ต้อง Join ใน Table ที่เราต้อง Join กันบ่อยครับ

สุดท้ายนี้ก็ขอบคุณเพื่อนๆที่อ่านบทความนี้จนจบนะครับ หวังเป็นอย่างยิ่งว่าบทความนี้จะเป็นประโยชน์กับเพื่อนๆแก๊ง Backend ด้วยกัน เจอกันในบทความหน้าๆนะครับ

--

--