กฎ
การออกแบบฐานข้อมูลให้ผ่านนอมอลฟอร์มขั้นที่ 1 ฐานข้อมูลจะต้องตรงตามเงื่อนไขทั้งสามข้อ คือ
- สร้างเทเบิล โดยแต่ละเทเบิลมีข้อมูลที่เกี่ยวข้องกันอยู่รวมกัน
- ในแต่ละเทเบิล จะต้องไม่มีแถวหรือบางส่วนของแถว (มองเฉพาะบ้างคอลัมน์) ที่ซ้ำกันและสอดคล้องกันหมดทุกแถว
- ระบุไพร์มารีคีย์ (Primary Key) ให้กับแต่ละเทเบิล
ก่อนอื่นต้องทำความเข้าใจก่อนว่า First Normal Form มีแค่กฎที่ใช้บอกว่า การออกแบบเทเบิลผ่านเกณฑ์แล้ว แต่ไม่ได้บอกกระบวนวิธีหรือขั้นตอนในการสร้างรวมไปถึงการออกแบบหรือแตกเทเบิล กระบวนวิธีที่ผู้เขียนเสนอจึงเป็นแค่คำแนะนำ ไม่ใช่ข้อบังคับ หากผู้ออกแบบเห็นว่ามีกระบวนวิธีที่ดีกว่า ก็ขอให้เลือกวิธีที่ดีกว่านั้น
ตัวอย่าง
กฎข้อแรก : สร้างเทเบิล โดยแต่ละเทเบิลมีข้อมูลที่เกี่ยวข้องกันอยู่รวมกัน
สมมติ ผู้เขียนเปิดร้านขายหนังสือ มีหลายสาขา ทุกครั้งที่มีลูกค้าซื้อหนังสือจะมีใบเสร็จจากเครื่องเก็บเงิน ต่อมาผู้เขียนเห็นว่าถึงเวลาต้องใช้ระบบไอทีเก็บข้อมูลเหล่านี้ลงคอมพิวเตอร์โดยตรง ซึ่งง่ายต่อการประมวลผล แทนที่การเก็บข้อมูลในใบเสร็จ
นี้คือข้อมูลใบเสร็จที่ใส่ลงไปในเทเบิลที่ชื่อว่า Order
Order Table
OrderDate | Branch | BranchPhone | EmployeeName | StartWorking | Item |
---|---|---|---|---|---|
1/1/2014 | Kaset | 021111111 | Uaychai | 1/1/2013 | SQL V1(IT) 1 unit 200 bath/unit
Jedi V1(Sci) 3 unit 100 bath/unit |
1/1/2014 | Kaset | 021111111 | Uaychai | 1/1/2013 | SQL V1(IT) 1 unit 200 bath/unit |
2/1/2014 | Kaset | 021111111 | Jereeya | 5/5/2013 | Jedi V1(Sci) 5 unit 100 bath/unit |
1/1/2014 | Ladprao | 027777777 | Somchai | 2/2/2013 | Jedi V1(Sci) 7 unit 100 bath/unit SQL V2(IT) 8 unit 200 bath/unit |
1/1/2014 | Ladprao | 027777777 | Somying | 9/9/2013 | Jedi V1(Sci) 3 unit 100 bath/unit Java V1(IT) 2 unit 400 bath/unit |
3/1/2014 | Ladprao | 027777777 | Somchai | 2/2/2013 | SQL V2(IT) 6 unit 200 bath/unit |
เทเบิลนี้ยังถือว่าเป็นรูปแบบของเทเบิลที่ไม่ถูกต้อง ด้วยเหตุผลสองประการ ประการแรก คอลัมน์ Item ที่แถวที่ 1, 4 และ 5 มีข้อมูลสองชุด ลักษณะของเทเบิลที่ถูกต้อง จะต้องมีข้อมูลเพียงหนึ่งตัวในหนึ่งคอลัมน์ของหนึ่งแถวเท่านั้น ประการที่สอง คอลัมน์ Item ของทุกแถวมีข้อมูลมากกว่าหนึ่งชนิด คือ ชื่อหนังสือ เลขเวอร์ชัน ประเภทหนังสือ จำนวนที่ซื้อ และ ราคาต่อหน่วย อยู่ในคอลัมน์เดียว ซึ่งเป็นเรื่องไม่ถูกต้อง ในหนึ่งคอลัมน์ควรมีข้อมูลเพียงชนิดเดียว
เทเบิล Order ที่ถูกต้องควรหน้าตาแบบนี้
Order Table
OrderDate | Branch | BranchPhone | EmployeeName | StartWorking | BookName | Ver | Category | Qty | Price/Unit |
---|---|---|---|---|---|---|---|---|---|
1/1/2014 | Kaset | 021111111 | Uaychai | 1/1/2013 | SQL | 1 | IT | 1 | 200 |
1/1/2014 | Kaset | 021111111 | Uaychai | 1/1/2013 | Jedi | 1 | Sci | 3 | 100 |
1/1/2014 | Kaset | 021111111 | Uaychai | 1/1/2013 | SQL | 1 | IT | 1 | 200 |
2/1/2014 | Kaset | 021111111 | Jereeya | 5/5/2013 | Jedi | 1 | Sci | 5 | 100 |
1/1/2014 | Ladprao | 027777777 | Somchai | 2/2/2013 | Jedi | 1 | Sci | 7 | 100 |
1/1/2014 | Ladprao | 027777777 | Somchai | 2/2/2013 | SQL | 2 | IT | 8 | 200 |
1/1/2014 | Ladprao | 027777777 | Somying | 9/9/2013 | Jedi | 1 | Sci | 3 | 100 |
1/1/2014 | Ladprao | 027777777 | Somying | 9/9/2013 | Java | 1 | IT | 2 | 400 |
3/1/2014 | Ladprao | 027777777 | Somchai | 2/2/2013 | SQL | 2 | IT | 6 | 200 |
กฎข้อสอง : ในแต่ละเทเบิล จะต้องไม่มีแถวหรือบางส่วนของแถว (มองเฉพาะบ้างคอลัมน์) ที่ซ้ำกันและสอดคล้องกันหมดทุกแถว
ในกรณีของเทเบิล Order ข้อมูลที่มีความซ้ำซ้อนจะอยู่ในคอลัมน์ {Branch, BranchPhone}, {Employee, StartWorking} และ {BookName, Ver, Category, Price/Unit} ข้อมูลที่ซ้ำซ้อนกันพวกนี้บ่งบอกถึงความเป็นอันหนึ่งอันเดียวกันของข้อมูลภายในชุดคอลัมน์ นั้นข้อมูลซ้ำซ้อนจะถูกแยกไว้อีกเทเบิลหนึ่ง จากนั้นให้การเช็คเทเบิลตัวใหม่ว่ามีข้อมูลซ้ำซ้อนอีกหรือไม่ ทำเช่นนี้ไปเรื่อยๆจนกว่าจะแน่ใจว่าไม่มีข้อมูลซ้ำซ้อนแล้ว สำหรับเทเบิลต้นทางคือเทเบิล Order ให้ปล่อยไปแบบนั้นก่อน ยังไม่ต้องทำอะไรจนกว่าจะถึงกฎข้อที่ 3 เทเบิลที่ได้ทั้งหมดจะเป็นเช่นนี้
Order Table
OrderDate | Branch | EmployeeName | BookName | Qty |
---|---|---|---|---|
1/1/2014 | Kaset | Uaychai | SQL | 1 |
1/1/2014 | Kaset | Uaychai | Jedi | 3 |
1/1/2014 | Kaset | Uaychai | SQL | 1 |
2/1/2014 | Kaset | Jereeya | Jedi | 5 |
1/1/2014 | Ladprao | Somchai | Jedi | 7 |
1/1/2014 | Ladprao | Somchai | SQL | 8 |
1/1/2014 | Ladprao | Somying | Jedi | 3 |
1/1/2014 | Ladprao | Somying | Java | 2 |
3/1/2014 | Ladprao | Somchai | SQL | 6 |
Branch Table
Branch | BranchPhone |
---|---|
Kaset | 021111111 |
Ladprao | 027777777 |
Employee Table
Employee | StartWorking |
---|---|
Uaychai | 1/1/2013 |
Jereeya | 5/5/2013 |
Somchai | 2/2/2013 |
Somying | 9/9/2013 |
Book Table
BookName | Ver | Category | Price/Unit |
---|---|---|---|
SQL | 1 | IT | 200 |
SQL | 2 | IT | 200 |
Jedi | 1 | Sci | 100 |
Java | 1 | IT | 400 |
กฎข้อที่สาม : ระบุไพร์มารีคีย์ (Primary Key) ให้กับแต่ละเทเบิล
ไพร์มารีคีย์ (ตัวย่อ : PK) คือคอลัมน์หรือชุดคอลัมน์จำนวนน้อยที่สุดที่ข้อมูลภายในคอลัมน์หรือชุดคอลัมน์นั้นไม่ซ้ำกันเลยในแต่ละแถว จุดประสงค์คือเพื่อหาว่าข้อมูลในคอลัมน์หรือชุดคอลัมน์ไหนจะใช้เป็นตัวแทนของแถวในเทเบิลได้ (อ่านรายละเอียดเพิ่มเติมได้ที่บทความอื่น)
เพิ่มเติม หากเทเบิลใหม่ที่แตกมา มีไพร์มารีย์คีย์คือทุกคอลัมน์รวมกัน ถือว่าเทเบิลใหม่ที่สร้างขึ้นมานั้น ไม่มีประโยชน์ในการลดขนาดให้กับเทเบิลต้นทาง ให้ทำลายเทเบิลนั้นทิ้งเสีย
การหาไพร์มารีคีย์ สำหรับเทเบิล Branch, Employee และ Book นั้นไม่ยาก ผลที่ได้จะเป็นดังนี้
Branch Table
Branch (PK) | BranchPhone |
---|---|
Kaset | 021111111 |
Ladprao | 027777777 |
Employee Table
Employee (PK) | StartWorking |
---|---|
Uaychai | 1/1/2013 |
Jereeya | 5/5/2013 |
Somchai | 2/2/2013 |
Somying | 9/9/2013 |
Book Table
BookName (PK) | Ver (PK) | Category | Price/Unit |
---|---|---|---|
SQL | 1 | IT | 200 |
SQL | 2 | IT | 200 |
Jedi | 1 | Sci | 100 |
Java | 1 | IT | 400 |
สำหรับเทเบิล Order ซึ่งเป็นเทเบิลต้นทาง เมื่อเทเบิลที่แตกออกมาสามารถหาไพร์มารีย์คีย์ได้ ข้อมูลของคอลัมน์ใดในเทเบิล Order ที่ถูกนำไปใช้เป็นไพร์มารีย์คีย์ในเทเบิลใหม่ก็ให้คงไว้ในเทเบิล Order และจะเรียกคอลัมน์นั้นว่าฟอร์เรนคีย์ (Foreign Key : FK) ส่วนคอลัมน์ใดที่แตกออกมาอยู่ในเทเบิลใหม่แต่ไม่ได้ถูกนำใช้เป็นไพร์มารีคีย์ ให้ทำการลบคอลัมน์ใน Order นั้นทิ้ง การทำแบบนี้จะทำให้เทเบิล Order มีขนาดเล็กลง ข้อมูลที่สูญหายไปจะไปอยู่เทเบิลใหม่ โดยใช้ FK ในเทเบิล Order เชื่อมกับ PK ในเทเบิลใหม่เป็นตัวเชื่อมประสาน
เทเบิล Order ใหม่ที่ได้จะเป็นแบบนี้
Order Table
ORDERDATE | BRANCH (FK-Branch) | EMPLOYEENAME (FK-Employee) | BOOKNAME(FK-Book) | Ver (FK-Book) | QTY |
---|---|---|---|---|---|
1/1/2014 | Kaset | Uaychai | SQL | 1 | 1 |
1/1/2014 | Kaset | Uaychai | Jedi | 1 | 3 |
1/1/2014 | Kaset | Uaychai | SQL | 1 | 1 |
2/1/2014 | Kaset | Jereeya | Jedi | 1 | 5 |
1/1/2014 | Ladprao | Somchai | Jedi | 1 | 7 |
1/1/2014 | Ladprao | Somchai | SQL | 2 | 8 |
1/1/2014 | Ladprao | Somying | Jedi | 1 | 3 |
1/1/2014 | Ladprao | Somying | Java | 1 | 2 |
3/1/2014 | Ladprao | Somchai | SQL | 2 | 6 |
จากนั้นให้ทำการหาไพรมารีย์คีย์ของเทเบิล Order ซึ่งพบว่าคอลัมน์หรือชุดคอลัมน์ใดเลยที่มีคุณลักษณะที่ใช้เป็นไพร์มารีคีย์ได้ ในกรณีให้สร้างคอลัมน์พิเศษขึ้นมาหนึ่งคอลัมน์แล้วให้ข้อมูลในคอลัมน์เป็นข้อมูลสมมติที่ไม่ซ้ำกัน ในที่นี้คือคอลัมน์ ID โดยให้ข้อมูลในคอลัมน์เป็นตัวเลขเรียงลำดับ ผลที่ได้จะเป็นดังนี้
Order Table
ID | ORDERDATE | BRANCH (FK-Branch) | EMPLOYEENAME (FK-Employee) | BOOKNAME(FK-Book) | Ver (FK-Book) | QTY |
---|---|---|---|---|---|---|
1 | 1/1/2014 | Kaset | Uaychai | SQL | 1 | 1 |
2 | 1/1/2014 | Kaset | Uaychai | Jedi | 1 | 3 |
3 | 1/1/2014 | Kaset | Uaychai | SQL | 1 | 1 |
4 | 2/1/2014 | Kaset | Jereeya | Jedi | 1 | 5 |
5 | 1/1/2014 | Ladprao | Somchai | Jedi | 1 | 7 |
6 | 1/1/2014 | Ladprao | Somchai | SQL | 2 | 8 |
7 | 1/1/2014 | Ladprao | Somying | Jedi | 1 | 3 |
8 | 1/1/2014 | Ladprao | Somying | Java | 1 | 2 |
9 | 3/1/2014 | Ladprao | Somchai | SQL | 2 | 6 |
จากนั้นดูให้แน่ใจว่า เทเบิลทุกตัวได้ผ่านกฎทั้งสามข้อของนอมอลฟอร์มขั้นที่ 1 ถ้าผ่านหมด ก็ถือว่าสิ้นสุดการทำ