Basic Database System Part 3 - Normalization

Basic Database System Part 3 - Normalization

สำหรับตอนนี้เราจะมาพูดถึง Normalization ว่ามันคืออะไร จากนั้นเราจะมาดูตัวอย่างของปัญหาจนทำให้เราต้องมาทำ Normalization กันครับ แล้วก็ตามที่ผมบอกไปนะครับที่ผมเขียนตรงนี้อาจไม่ถูกต้องตามทฤษฎี 100% คุณสามารถอ่านมันเป็นแนวทางเริ่มต้นครับ จากนั้นพอเข้าใจแล้วลองไปหาหนังสือเกี่ยวกับ Database จริงๆมาอ่าน หรือถ้าได้มีโอกาสเรียนกับอาจารย์ก็ขอให้ตั้งใจเรียนและถามสิ่งที่สงสัยกับอาจารย์ที่สอนนะครับ

Normalization

จริงๆผมอยากเอาปัญหาตั้งก่อนแล้วค่อยบอกว่า Normalization แต่คิดไปคิดมาอธิบายก่อนเลยดีกว่าว่ามันคืออะไรเพราะมันสั้นมากๆ โดย Normalization คือกระบวนการในการออกแบบ Database เพื่อไม่ให้เกิดปัญหาเกี่ยวกับ INSERT , UPDATE , DELETE และทำให้ข้อมูลที่เราเก็บนั้นไม่มีความซ้ำซ้อนของข้อมูล

ปัญหา INSERT , UPDATE , DELETE

ส่วนนี้เรามาดูว่า Table (Relation) ที่เราคิดออกแบบขึ้นมาแล้วยังไม่ผ่านกระบวนการ Normalization จะมีปัญหาอะไรบ้าง

Table : CURRENCY_TRADING_SYSTEM

Table :  CURRENCY_TRADING_SYSTEM

สมมุติว่าผมต้องการทำ Application แลกเปลี่ยนเงินตราระหว่างประเทศขึ้นมา เรา Design Table : CURRENCY_TRADING_SYSTEM ขึ้นมาเพื่อเก็บข้อมูลว่า User ไหนเก็บเงินสกุลไหนไว้บ้าง ซึ่งผมก็ออกแบบ Table ไว้เก็บข้อมูลดังภาพ โดยแต่ละ Column จะเก็บข้อมูล

  • USERNAME : เก็บ USERNAME ของผู้ใช้
  • EMAIL : เก็บ EMAIL ของผู้ใช้
  • USER_TYPE : เก็บว่าผู้ใช้นี้เป็น TYPE ไหน
  • PRIORITY : ค่าความสำคัญของ USER โดยความสำคัญจะขึ้นอยู่กับว่าเป็น USER TYPE ไหน โดยค่ายิ่งมากยิ่งสำคัญ
  • CURRENCY_CODE : CODE ของสกุลเงินนั้น
  • CURRENCY_NAME : ชื่อของสกุลเงินนั้น
  • COUNTRY : ชื่อประเทศเจ้าของสกุลเงินนั้น
  • AMOUNT : ผู้ใช้มีเงินสกุลนั้นเท่าไหร่

Table นี้มี Primary key คือ USERNAME , CURRENTCY_CODE ซึ่งคุณสามารถตรวจสอบได้โดยเอา USERNAME + CURRENCY_CODE ไปทำการค้นหาคุณจะเจอแค่ 1 ROW เสมอ และมันก็ตรงตาม Business Logic คือ USER หนึ่งคนก็ควรมีข้อมูลการถือเงินสกุลใดสกุลหนึ่งแค่ 1 ข้อมูลเท่านั้น มันคงจะแปลกๆถ้าบอกว่า USER : surapong มีเงิน THB 2 จำนวน

ปัญหาการ Insert

ตอนเราเห็น Table นี้เราอาจจะไม่เห็นปัญหาของมัน แต่เมื่อเราเริ่มใช้งานเนี่ยเราจะเริ่มเจอปัญหา ปัญหาแรกที่เราจะเจอมี 2 ปัญหาคือ

  1. INSERT ข้อมูลไม่ได้
  2. INSERT ข้อมูลที่ไม่ถูกต้องเข้าไปได้

เรามาดูทีละปัญหา

INSERT ข้อมูลไม่ได้

ลองนึกภาพตามนะครับว่า ถ้าคุณอยากจะ Add User ใหม่เข้ามาในระบบ แต่ User นั้นยังไม่ได้โอนเงินเข้ามาในระบบคุณเลยสักสกุลเงิน ดังนั้นข้อมูลที่คุณจะ Insert เข้าไปก็จะมีหน้าตาประมาณนี้

ข้อมูล User ที่ต้องการ Insert

ซึ่งมันเหมือนจะ Insert ได้ใช่ไหมครับ แต่จริงๆมัน Insert ไม่ได้ครับ เพราะกฎของ Relational Database Model ที่ตั้งไว้ว่า Primary key must not be null พอตัว CURRENCY_CODE มีค่าเป็น NULL ข้อมูลนี้จึงไม่สามารถ INSERT ได้

ในทางกลับกันถ้าคุณอยากเพิ่มสกุลเงินชนิดใหม่เข้าไปเก็บใน Database ซึ่งเงินพอเป็นเงินสกุลใหม่ก็แปลว่าไม่มีคนเคยซื้อเงินสกุลนี้เลย ข้อมูลที่ Insert เข้าไปก็จะเป็น

ข้อมูล Currency ที่ต้องการ Insert

ซึ่งจะเห็นว่า USERNAME = NULL ก็แปลว่าไม่สามารถ INSERT ข้อมูลได้ครับ

INSERT ข้อมูลที่ไม่ถูกต้องเข้าไปได้

ถ้าสมมุติคุณอยากจะเพิ่มข้อมูลการซื้อสกุลเงินใหม่ของ USER : surapong เข้าไป แต่ตอนบันทึกนั้น เขาใส่ข้อมูล email ผิดลงไปดังตัวอย่าง

ข้อมูล User ที่ต้องการ Insert ที่มีข้อมูล Email ไม่ถูกต้อง

ซึ่งจากตัวอย่างนั้นข้อมูลสามารถเพิ่มลงไปใน Database ได้ แต่ปัญหาที่เกิดขึ้นคือความถูกต้องของข้อมูล คุณจะเห็นได้ว่าตอนนี้ USER : surapong เนี่ยมี EMAIL ที่ 2 EMAIL คือ surapong_inwza007@hotmail.com , surapong_007inwza@hotmail.com ซึ่งคำถามคือตกลง EMAIL ไหนเป็น EMAIL ที่ถูก สมมติถ้าต้องส่ง EMAIL ไปให้ user คนนั้นเราจะต้องใช้ EMAIL ไหนล่ะ ปัญหาข้อมูลไม่ถูกต้องไม่สัมพันธ์กันนี้มีชื่อเรียกว่า Data inconsistency

ปัญหาการ Delete

ปัญหาการ Delete ที่เราจะเจอนั้นคือปัญหาการลบข้อมูลที่ไม่ได้อยากลบให้หายไปด้วย อ่านแล้วอาจจะไม่เข้าใจ เรามาลองดูตัวอย่างครับ

ข้อมูลที่ต้องการ Delete

จากภาพเราต้องการลบ Row ที่ล้อมกรอบสีแดงไว้ ซึ่งก็เป็นเรื่องปกติที่จะลบข้อมูลออกเพราะ User ทำการถอนเงินออกไปแล้ว แต่ปัญหาอยู่ที่เมื่อเราลบข้อมูล Row นี้ทิ้งไป ข้อมูลเกี่ยวกับสกุลเงิน JPY (Yen) จะหายไปจาก Database ด้วย ซึ่งนั่นเป็นสิ่งที่เราไม่ต้องการ

ปัญหาการ Update

ตัวอย่างข้อมูลที่ซ้ำซ้อน

ปัญหานี้เกิดจากที่มีข้อมูลซ้ำซ้อนอยู่ใน Table จากตัวอย่างคุณจะเห็นว่าข้อมูล email ของ USER นั้นแสดงซ้ำ คราวนี้เวลาจะต้องแก้ไขข้อมูล email เช่น ต้องการเปลี่ยน email ของ USER : sarun เป็น sarun@gmail.com ก็ต้องทำการ UPDATE ทุก Row ที่ USERNAME = sarun ซึ่งถ้าเป็นคำสั่ง SQL ก็จะเป็นแบบนี้

1
2
3
UPDATE CURRENCY_TRADING_SYSTEM
SET EMAIL = 'sarun@gmail.com'
WHERE USERNAME = 'sarun'

ถามว่ามันเป็นปัญหายังไง ปัญหาคือเมื่อมันมีหลาย ROW ถ้าระหว่างที่กำลัง UPDATE แต่ละ ROW อยู่นั้นตัว DBMS นั้นเกิดปัญหา ก็แปลว่า ข้อมูล email ของ USER : sarun นั่นเองจะมี email เก่ากับ email ที่ถูกแก้ไขไปแล้วอยู่ ซึ่งนั่นก่อให้เกิดปัญหา Data inconsistency ครับ แต่น้่นเป็นเรื่องเมื่ออดีตครับ ในปัจจุบันนั้น DBMS นั้นมี TRANSACTION PROCESS ซึ่งนั่นก็แปลว่า ถ้าเกิดปัญหาระหว่างการ UPDATE ตัวระบบก็จะ ROLLBACK ข้อมูลกลับไปเหมือนว่าไม่เคยสั่ง UPDATE ดังนั้นปัญหาเรื่อง Data inconsistency จากการ UPDATE จะไม่เกิดขึ้นครับ

แล้วเราจะแก้ปัญหาพวกนี้ยังไง

วิธีแก้ปัญหา INSERT , UPDATE , DELETE ก็คือการทำ Normalization ครับ โดยกระบวนการทำนั้นผมจะยังไม่อธิบายในตอนนี้ เพราะถ้าอธิบายในตอนนี้มันจะยาวมากเพราะต้องยกตัวอย่างให้เห็น ดังนั้นเราแสดงผลลัพธ์จากการทำ Normalization ก่อนเพื่อให้เห็นว่ามันสามารถแก้ปัญหา INSERT , UPDATE , DELETE ได้จริง

ผลลัพธ์จากการทำ Normalization

Table ที่เกิดจากการทำ Normalization

ผลลัพธ์จากการทำ Normalization นั้นจะเปลี่ยนจาก Table : CURRENCY_TRADING_SYSTEM มาเป็น 4 Table คือ

  • USER : Table นี้เก็บข้อมูลของ User
  • USER_TYPE : Table นี้เก็บข้อมูลของ USER TYPE
  • CURRENCY : Table นี้เก็บข้อมูลของ CURRENCY
  • USER_CURRENCY : Table นี้เก็บข้อมูลว่า USER นั้นมี CURRENCY ใดอยู่บ้าง

ทดสอบกับปัญหาการ Insert

ทดสอบกับปัญหา Insert ไม่ได้

หากเราต้องการ Insert User ใหม่เข้าไปในระบบเราสามารถ Insert ข้อมูลไปที่ Table : User ได้เลยเพราะไม่ติดปัญเรื่อง Primary key must not be null แล้ว

ข้อมูล User ใหม่ที่ต้องการ Insert

ทดสอบปัญหา INSERT ข้อมูลที่ไม่ถูกต้องเข้าไปได้

ปัญหานี้จะถูกแก้เพราะเมื่อเราต้องการ Insert ข้อมูว่า User : surapong มีสกุลเงินใหม่เพิ่มเข้าไปนั้นจะทำการเพิ่มที่ Table : USER_CURRENCY ซึ่ง Table นี้จะทำการกรอกแค่ USERNAME , CURRENCY , AMOUNT เท่านั้น ดังนั้นจะไม่มีทางกรอกข้อมูลที่ไม่ถูกต้องไม่สอดคล้องลงไปได้

ข้อมูล USER_CURRENCY ใหม่ที่ต้องการ Insert

ทดสอบกับปัญหาการ Delete

ปัญหาการ Delete แล้วลบข้อมูลที่ไม่อยากลบจะไม่เกิดขึ้นเพราะเวลาลบข้อมูลเราลบที่ Table : USER_CURRENCY ซึ่งเมื่อลบไปแล้วข้อมูลของ CURRENCY ก็ยังคงอยู่ไม่ได้หายไป

ข้อมูลที่ต้องการลบ

อยากแสดงข้อมูลเหมือน Table : CURRENCY_TRADING_SYSTEM จะทำยังไง

ถ้าเราอยากแสดงข้อมูลเหมือนกับ Table : CURRENCY_TRADING_SYSTEM นั้นสามารถทำได้โดยการ SELECT แล้วในคำสั่ง SELECT เราสั่งให้ทำการ JOIN TABLE ที่เกี่ยวข้องเข้าด้วยกัน โดยจากตัวอย่างเราจะได้คำสั่ง SQL แบบนี้

1
2
3
4
5
SELECT *
FROM USER_CURRENCY t1
INNER JOIN USER t2 ON t1.USERNAME = t2.USERNAME
INNER JOIN CURRENCY t3. ON t1.CURRENCY_CODE = t3.CURRENCY_CODE
INNER JOIN USER_TYPE t4 ON t2.USER_TYPE = t4.USER_TYPE

ปัญหาใหม่

เมื่อทำ Normalization เพื่อออกแบบ Database ให้ไม่ให้เกิดปัญหาการ INSERT , DELETE , UPDATE ซึ่งผลลัพธ์ที่ได้คือเปลี่ยนจาก 1 Table กลายเป็น 4 Table ปัญหาที่ตามมาก็คือเมื่อต้องการแสดงข้อมูลให้เหมือนกับ Table : CURRENCY_TRADING_SYSTEM นั้นจะต้องทำการ JOIN ซึ่งเมื่อทำการ JOIN ก็จะเกิดปัญหาเรื่องต้องใช้ Resource มาทำการ JOIN ซึ่งเป็นปัญหาด้าน Performance

พอเป็นแบบนี้คุณก็จะบอกว่า อ้าวแล้วทำไปทำไมวะ คำตอบก็คือ ปัญหาเรื่อง Resource ที่ใช้ในการ JOIN ซึ่งเป็นปัญหาเรื่อง Performance นั้นสามารถแก้ไขได้ด้วยการเพิ่ม Hardware หรือใช้วิธีการ Tuning DBMS ให้สามารถ JOIN ได้เร็วขึ้นอาจจะทำ INDEX แบ่ง Partition data ต่างๆนาๆ แต่ปัญหา INSERT , UPDATE , DELETE และปัญหา Data inconsistency นั้นไม่สามารถแก้ได้ด้วย Hardware หรือการ Tuning DBMS แก้ได้ วิธีแก้คือต้องไปไล่แก้ข้อมูลให้ถูกต้องหรือไปเพิ่มเงื่อนไขในการทำงานเช่น ถ้าสมัคร User ใหม่ต้องทำการซื้อสกุลเงินหนึ่งที่มีในระบบเลย หรือถ้าต้องการเพิ่มสกุลเงินใหม่ก็ต้องมีคนซื้อสกุลเงินนั้นอย่างน้อยหนึ่งคน ซึ่งอันนี้ยังพอเป็นไปได้ แต่ถ้าเรื่อง Data inconsistency คุณจะทำยังไงในเมื่อข้อมูลมันไม่ถูกต้องสอดคล้องกัน คุณจะโทรไปหา User แต่ละคนเพื่อถามว่า EMAIL ของคุณคืออันไหนกันแน่เหรอ หรือถ้าไม่โทรคุณจะเอาอะไรตัดสินใจว่าอันไหนถูก หากลองคิดแบบนี้แล้วคุณคิดว่าเราควรทำหรือไม่ควรทำ Normalization ล่ะ เพราะปัญหาหนึ่งแก้ได้ด้วยเงินกับความรู้เทคนิค อีกปัญหาหนึ่งต้องสร้างเงื่อนไขที่ทำให้การดำเนินธุรกิจมีความยากขึ้น

สรุป

สำหรับตอนนี้เราได้รู้แล้วว่า Normalization คืออะไร มันมีไว้เพื่อแก้ปัญหาอะไร แล้วมันจำเป็นต้องทำไหม เมื่อเราเข้าใจที่มาที่ไปและเหตุผลแล้ว ในตอนต่อไปเราจะไปดูว่าการทำ Normalization นั้นทำอย่างไรกันครับ