วางข้อมูลในที่ที่ควรอยู่
มีให้อ่านอีก 28 ภาษา
English · Español · 日本語 · Português (Brasil) · Français · Deutsch · Italiano · 한국어 · 简体中文 · Português (Portugal) · 繁體中文 · Bahasa Indonesia · Türkçe · Polski · Tiếng Việt · Nederlands · العربية · עברית · Українська · हिन्दी · Русский · Svenska · Dansk · Norsk bokmål · Suomi · Español (México) · Français (Canada) · Deutsch (Schweiz)

โมดูลที่ 4 จาก 6 ในบท การสร้างโมเดลข้อมูลเพื่อความหมาย บทที่ 2 ของหลักสูตร Data Architect แต่ละโมดูลอ่านแยกกันได้ ก่อนหน้า: สร้างโมเดลเชิงตรรกะ · ถัดไป: ออกแบบถึงรูปแบบบรรทัดฐานที่สาม แล้วถอยออกมา
คำถามที่โมดูลนี้ตอบ: normalization ช่วยให้ผมรู้ได้อย่างไรว่าข้อมูลควรอยู่ที่ไหนจริงๆ?
เรามีโมเดลเชิงตรรกะแล้ว: เอนทิตี ความสัมพันธ์ แอตทริบิวต์ และคีย์ ตอนนี้ผมอยากถามหนึ่งในคำถามที่สำคัญที่สุดในการสร้างโมเดลข้อมูล: ข้อมูลนี้ควรอยู่ตรงนี้จริงหรือ?
ฟังดูง่าย แต่ไม่ใช่ ผมเห็นระบบมากมายที่ทุกฟิลด์สมเหตุสมผลในตอนที่มีคนสร้างมัน ปัญหาไม่ใช่ว่าฟิลด์เหล่านั้นไร้สาระ แต่คือข้อเท็จจริงถูกเก็บไว้ผิดที่ การจัดส่งเก็บข้อมูลที่จริงๆ แล้วอธิบายศูนย์กระจายสินค้า ที่อยู่เดียวกันปรากฏบนเรกคอร์ดหลายร้อยรายการ ฟิลด์เดียวเก็บสามค่าคั่นด้วยจุลภาค การเปลี่ยนข้อเท็จจริงทางธุรกิจข้อเดียวหมายถึงการอัปเดต 10,000 เรกคอร์ด นั่นคือปัญหาที่ normalization (การทำให้เป็นบรรทัดฐาน) ช่วยเราแก้ และก่อนที่เราจะจมอยู่กับศัพท์ฐานข้อมูล นี่คือแนวคิดทั้งหมด: วางข้อเท็จจริงไว้ในที่ที่มันควรอยู่ ทุกอย่างที่เหลือในโมดูลนี้ช่วยให้เราทำเช่นนั้นได้อย่างเป็นระบบมากขึ้น
Normalization คืออะไร?
หัวข้อที่มีชื่อว่า “Normalization คืออะไร? ”การทำให้เป็นบรรทัดฐาน (normalization) คือการจัดระเบียบข้อมูลให้ข้อเท็จจริงแต่ละข้อถูกเก็บไว้ครั้งเดียว ในที่ที่มันควรอยู่ เมื่อตารางหนึ่งทำงานหลายอย่างเกินไป เราแยกมันออกเป็นตารางที่แต่ละตารางอธิบายสิ่งเดียว แล้วเชื่อมพวกมันด้วยคีย์
ทำไมต้องลำบาก? เพราะข้อเท็จจริงที่เก็บครั้งเดียวคือข้อเท็จจริงที่เราเปลี่ยนครั้งเดียว normalization ป้องกันความซ้ำซ้อน (redundancy) คือข้อเท็จจริงเดียวกันที่ถูกคัดลอกไปทั่วหลายเรกคอร์ด และป้องกันความไม่สอดคล้องที่แทรกเข้ามาเมื่อสำเนาหนึ่งถูกอัปเดตแต่สำเนาอื่นไม่ถูกอัปเดต นั่นคือเหตุผลที่มันเหมาะกับระบบธุรกรรมอย่าง Salesforce ที่เรกคอร์ดถูกสร้างและอัปเดตตลอดทั้งวัน
เราทำ normalization เป็นขั้นๆ ที่เรียกว่า รูปแบบบรรทัดฐาน (normal form) และแต่ละขั้นถามคำถามเพียงข้อเดียว:
- รูปแบบบรรทัดฐานที่หนึ่ง (1NF): หนึ่งค่าต่อหนึ่งฟิลด์ และมีบางอย่างที่ระบุทุกแถวได้อย่างไม่ซ้ำ
- รูปแบบบรรทัดฐานที่สอง (2NF): ข้อเท็จจริงทุกข้ออธิบายสิ่งที่ระบุแถวนั้นทั้งหมด ไม่ใช่แค่บางส่วน
- รูปแบบบรรทัดฐานที่สาม (3NF): ข้อเท็จจริงทุกข้ออธิบายสิ่งนี้ ไม่ใช่สิ่งอื่นที่มันสัมพันธ์ด้วย
ยังมีรูปแบบอื่นที่ไปไกลกว่าขั้นที่สาม ผมจะพูดถึงมันสักเล็กน้อยทีหลัง และพูดถึงว่าทำไมเรามักหยุดที่ขั้นที่สาม ก่อนอื่น มาดูสามขั้นนี้เกิดขึ้นจริงกัน
ทำไมเราจึงทำ Normalization?
หัวข้อที่มีชื่อว่า “ทำไมเราจึงทำ Normalization? ”เวลาผมสอนสด ผมไม่ได้เริ่มจากการจัดส่ง ผมเริ่มจากสุนัข ทุกคนเข้าใจสุนัข จึงไม่มีใครถูกเรื่องธุรกิจดึงความสนใจไป และเราได้เห็นข้อมูลพยายามหาบ้านของมัน จากนั้นเราจึงนำวิธีเดียวกันไปใช้กับ Steve Trucking
นั่ง คอย โมเดล
หัวข้อที่มีชื่อว่า “นั่ง คอย โมเดล ”เริ่มต้น: สเปรดชีตดิบ ผมจะใส่สุนัขสักสองสามตัวขึ้นจอ แล้วคนในห้องก็เพิ่มสุนัขของพวกเขา ภายในไม่กี่นาทีเราก็มีรายการยาวรายการเดียว: สุนัข ปีที่เกิด ใครเป็นเจ้าของ เจ้าของอยู่ที่ไหน สายพันธุ์ ใครเป็นผู้เพาะพันธุ์ และท่าที่มันทำได้
| สุนัข | ปีเกิด | เจ้าของ | รหัสไปรษณีย์ | เมือง | สายพันธุ์ | ผู้เพาะพันธุ์ | ท่า |
|---|---|---|---|---|---|---|---|
| Bella | 2017 | Marcus | 80202 | Denver | ลาบราดอร์ | Maple Ridge | นั่ง, คอย, ยื่นมือ |
| Milo | 2019 | Marcus | 80202 | Denver | พุดเดิ้ล | Golden Meadow | นั่ง, กลิ้งตัว |
| Max | 2022 | Ana | 64105 | Kansas City | พันธุ์ผสม | นั่ง, แกล้งตาย | |
| Luna | 2020 | Kenji | 98101 | Seattle | ฮัสกี้ | Cascade | นั่ง, คอย, หมุนตัว |
| Rocky | 2024 | Priya | 30301 | Atlanta | บีเกิล | Maple Ridge | นั่ง |
| Coco | 2014 | Ana | 64105 | Kansas City | ชิวาวา | Little Paws | ยื่นมือ, หมุนตัว |
มันใช้ได้ สเปรดชีตทุกอันใช้ได้ในวันแรก แต่ดูสิ่งที่ผิดอยู่แล้ว ผมจะหาสุนัขทุกตัวที่ทำท่ายื่นมือได้อย่างไร? รหัสไปรษณีย์ของ Ana อยู่ในสองแถว ของ Marcus ก็เช่นกัน: จะเกิดอะไรขึ้นถ้ามีคนอัปเดตแถวหนึ่งแต่ไม่อัปเดตอีกแถว? Maple Ridge ปรากฏสองครั้ง Kansas City ก็เช่นกัน และ Max มาจากสถานพักพิงสัตว์ จึงไม่มีผู้เพาะพันธุ์เลย
ขั้นที่ 1: รูปแบบบรรทัดฐานที่หนึ่ง กฎสองข้อ แต่ละฟิลด์เก็บค่าเดียว รายการท่าที่คั่นด้วยจุลภาคจึงต้องไป และแต่ละแถวต้องมีบางอย่างที่ระบุมันได้อย่างไม่ซ้ำ ไม่ใช่ชื่อสุนัข มีสุนัขชื่อ Max อยู่เยอะมาก แต่เป็น ID: ID ที่สร้างขึ้นอัตโนมัติหรือ GUID ท่าถูกแยกออกมาเป็นแถวของตัวเอง หนึ่งแถวต่อสุนัขหนึ่งตัวกับท่าหนึ่งท่า และเมื่อท่าหนึ่งมีแถวของตัวเองแล้ว ก็มีที่สำหรับสิ่งที่เราไม่เคยบอกได้ในรายการคั่นจุลภาค: ท่านั้นยากแค่ไหน สุนัขตัวนี้ทำได้ดีแค่ไหน และมันเรียนท่านั้นเมื่อไร
สุนัข (ระบุด้วย ID สุนัข)
| ID สุนัข | ชื่อ | ปีเกิด | เจ้าของ | รหัสไปรษณีย์ | เมือง | สายพันธุ์ | ผู้เพาะพันธุ์ |
|---|---|---|---|---|---|---|---|
| D-001 | Bella | 2017 | Marcus | 80202 | Denver | ลาบราดอร์ | Maple Ridge |
| D-002 | Milo | 2019 | Marcus | 80202 | Denver | พุดเดิ้ล | Golden Meadow |
| D-003 | Max | 2022 | Ana | 64105 | Kansas City | พันธุ์ผสม | |
| D-004 | Luna | 2020 | Kenji | 98101 | Seattle | ฮัสกี้ | Cascade |
| D-005 | Rocky | 2024 | Priya | 30301 | Atlanta | บีเกิล | Maple Ridge |
| D-006 | Coco | 2014 | Ana | 64105 | Kansas City | ชิวาวา | Little Paws |
ท่าของสุนัข (ระบุด้วย ID สุนัข + ท่า)
| ID สุนัข | ท่า | ความยาก | ระดับทักษะ | เรียนเมื่อ |
|---|---|---|---|---|
| D-001 | นั่ง | ง่าย | เชี่ยวชาญ | 2017-11 |
| D-001 | คอย | ง่าย | เชี่ยวชาญ | 2018-01 |
| D-001 | ยื่นมือ | ปานกลาง | ดี | 2018-06 |
| D-002 | นั่ง | ง่าย | เชี่ยวชาญ | 2020-02 |
| D-002 | กลิ้งตัว | ยาก | กำลังเรียน | 2023-05 |
| D-003 | นั่ง | ง่าย | ดี | 2022-06 |
| D-003 | แกล้งตาย | ยาก | กำลังเรียน | 2024-03 |
| D-004 | นั่ง | ง่าย | เชี่ยวชาญ | 2020-11 |
| D-004 | คอย | ง่าย | ดี | 2021-01 |
| D-004 | หมุนตัว | ปานกลาง | เชี่ยวชาญ | 2021-06 |
| D-005 | นั่ง | ง่าย | กำลังเรียน | 2025-02 |
| D-006 | ยื่นมือ | ปานกลาง | เชี่ยวชาญ | 2015-06 |
| D-006 | หมุนตัว | ปานกลาง | กำลังเรียน | 2025-04 |
ตอนนี้ “สุนัขทุกตัวที่ทำท่ายื่นมือได้” กลายเป็นคำถามง่ายๆ แต่สังเกตว่าท่าของสุนัขถูกระบุด้วยอะไร: ไม่ใช่สิ่งเดียว แต่เป็นสองสิ่งรวมกัน คือสุนัขและท่า จำเรื่องนั้นไว้
ขั้นที่ 2: รูปแบบบรรทัดฐานที่สอง รูปแบบบรรทัดฐานที่สองมีความหมายก็ต่อเมื่อแถวหนึ่งถูกระบุด้วยสิ่งที่ผสมกัน และท่าของสุนัขก็เป็นเช่นนั้น ผมจึงถามกับทุกคอลัมน์ว่า: มันอธิบายทั้งคู่ หรือแค่บางส่วน? ระดับทักษะอธิบายสุนัขตัวนี้ที่ทำท่านี้: Bella เชี่ยวชาญท่านั่ง Rocky ยังกำลังเรียนอยู่ วันที่เรียนก็เช่นกัน แต่ความยากล่ะ? ท่านั่งง่ายไม่ว่าสุนัขตัวไหนจะทำ มันอธิบายท่า ซึ่งเป็นแค่บางส่วนของคีย์ และนั่นคือเหตุผลที่ “ง่าย” ซ้ำอยู่ในทุกแถวของท่านั่ง ท่าจึงได้ตารางของตัวเอง และสิ่งที่เหลือคือคู่นั้น
ท่า
| ID ท่า | ท่า | ความยาก |
|---|---|---|
| T-01 | นั่ง | ง่าย |
| T-02 | คอย | ง่าย |
| T-03 | ยื่นมือ | ปานกลาง |
| T-04 | กลิ้งตัว | ยาก |
| T-05 | แกล้งตาย | ยาก |
| T-06 | หมุนตัว | ปานกลาง |
ท่าของสุนัข (ID สุนัข + ID ท่า)
| ID สุนัข | ID ท่า | ระดับทักษะ | เรียนเมื่อ |
|---|---|---|---|
| D-001 | T-01 | เชี่ยวชาญ | 2017-11 |
| D-001 | T-02 | เชี่ยวชาญ | 2018-01 |
| D-001 | T-03 | ดี | 2018-06 |
| D-002 | T-01 | เชี่ยวชาญ | 2020-02 |
| D-002 | T-04 | กำลังเรียน | 2023-05 |
| D-003 | T-01 | ดี | 2022-06 |
| D-003 | T-05 | กำลังเรียน | 2024-03 |
| D-004 | T-01 | เชี่ยวชาญ | 2020-11 |
| D-004 | T-02 | ดี | 2021-01 |
| D-004 | T-06 | เชี่ยวชาญ | 2021-06 |
| D-005 | T-01 | กำลังเรียน | 2025-02 |
| D-006 | T-03 | เชี่ยวชาญ | 2015-06 |
| D-006 | T-06 | กำลังเรียน | 2025-04 |
ตารางท่าของสุนัขนั้นคือตารางที่เราพบในโมดูลที่ 2 และมันมีชื่อในการสร้างโมเดลข้อมูล: junction หรือที่ผมเรียกว่า ออบเจกต์เชื่อมเชิงตรรกะ (logical junction object) สุนัขหนึ่งตัวทำได้หลายท่า ท่าหนึ่งมีสุนัขหลายตัวทำได้ junction อยู่ระหว่างทั้งสอง และมันไม่ใช่แค่ท่อเปล่าๆ: มันเก็บข้อเท็จจริงที่เป็นของความสัมพันธ์เอง คือระดับทักษะและวันที่ เราจะเจอมันอีกในโมดูลที่ 6 และในบทที่ 3 เราจะเห็นว่า Salesforce ใช้คำว่า “junction object” ในความหมายที่แคบกว่า: Junction Object ของ Salesforce คือออบเจกต์ที่กำหนดเองที่มีความสัมพันธ์ master-detail สองชุด (บทที่ 3 โมดูลที่ 7)
ขั้นที่ 3: รูปแบบบรรทัดฐานที่สาม ทีนี้กลับไปที่ตารางสุนัข ทุกอย่างในนั้นถูกระบุด้วย ID สุนัข รูปแบบบรรทัดฐานที่สองจึงไม่มีอะไรจะพูด รูปแบบบรรทัดฐานที่สามถามคำถามที่ต่างออกไป: ข้อเท็จจริงนี้อธิบายสุนัข หรือสิ่งอื่นที่สุนัขสัมพันธ์ด้วย? สายพันธุ์เป็นสิ่งหนึ่งของตัวเอง: ลาบราดอร์ก็คือลาบราดอร์ไม่ว่าจะเป็นสุนัขตัวไหน และข้อเท็จจริงเกี่ยวกับมัน เช่นขนาด เป็นของสายพันธุ์ ไม่ใช่ของ Bella รหัสไปรษณีย์และเมืองอธิบายเจ้าของ ไม่ใช่สุนัข: นั่นคือที่ที่ Ana อาศัยอยู่ ไม่ว่าเราจะดูสุนัขตัวไหนของเธอ รายละเอียดของผู้เพาะพันธุ์อธิบายผู้เพาะพันธุ์ แต่ละอย่างเหล่านั้นคือข้อเท็จจริงเกี่ยวกับข้อเท็จจริงอื่น แต่ละอย่างจึงย้ายไปที่ตารางของตัวเอง และสุนัขชี้ไปหามันด้วย foreign key:
- สุนัข: ID สุนัข, ชื่อ, ปีเกิด, ID เจ้าของ, ID สายพันธุ์, ID ผู้เพาะพันธุ์
- เจ้าของ: ID เจ้าของ, ชื่อ, รหัสไปรษณีย์, เมือง
- สายพันธุ์: ID สายพันธุ์, สายพันธุ์, ขนาด
- ผู้เพาะพันธุ์: ID ผู้เพาะพันธุ์, ชื่อ, เมือง
- ท่า และท่าของสุนัข จากขั้นที่ 2
ตอนนี้ Ana ถูกเก็บไว้ครั้งเดียว โดยสุนัขทั้งสองตัวของเธอชี้มาที่เธอ Maple Ridge ถูกเก็บครั้งเดียว และตารางสายพันธุ์ให้บ้านแก่ข้อเท็จจริงเกี่ยวกับแต่ละสายพันธุ์ เช่นขนาด ซึ่งสเปรดชีตไม่มีที่ให้ใส่ foreign key ว่างได้: Max ไม่มี ID ผู้เพาะพันธุ์ และนั่นคือคำตอบที่ตรงไปตรงมา ไม่ใช่ช่องโหว่ จากนั้นผมโยนคำถามหลอกให้คนในห้อง: วันที่เจ้าของได้สุนัขมา วันที่นั้นควรอยู่ที่ไหน? บนสุนัข ในโมเดลนี้สุนัขมีเจ้าของได้ทีละหนึ่งคน วันที่นั้นจึงอธิบายความสัมพันธ์ของสุนัขกับเจ้าของ
คุณอาจไปอีกขั้นหนึ่งและแยกเมืองออกจากรหัสไปรษณีย์ได้ ตำราทำแบบนั้น ในทางปฏิบัติ ที่อยู่ควรอยู่ด้วยกัน และยังไงซะ Kansas City ก็อยู่ในสองรัฐ เป็นเครื่องเตือนใจที่ดีว่าโลกจริงไม่ได้อ่านตำราของเรา
นั่นคือลำดับขั้น: สเปรดชีตหนึ่งอัน แล้วคำถามสามข้อ และข้อเท็จจริงทุกข้อก็ไปอยู่ในที่เดียว ถอยออกมาดูสิ่งที่เราสร้าง รายการรกๆ หนึ่งรายการกลายเป็นหกตาราง แต่ละตารางอธิบายสิ่งเดียว เชื่อมกันด้วยคีย์:
ไม่มีใครต้องใช้คำศัพท์ Salesforce สักคำเพื่อตามเรื่องนั้นทัน มันเป็นแค่สุนัข คนรอบตัวพวกมัน และสิ่งที่พวกมันทำได้
ทีนี้นำไปใช้กับ Steve Trucking
หัวข้อที่มีชื่อว่า “ทีนี้นำไปใช้กับ Steve Trucking ”สุนัขทำให้เห็นขั้นตอนได้ง่าย ธุรกิจจริงทำให้มันมีความสำคัญ เพราะความผิดพลาดแบบเดียวกันมีต้นทุนเป็นเงินจริงเมื่ออยู่ในระดับขนาดใหญ่ ลองนำคำถามสามข้อเดียวกันไปที่ Steve Trucking สมมติว่าบริษัทเก็บข้อมูลการจัดส่งแบบนี้:
| การจัดส่ง | พันธมิตรค้าปลีก | ศูนย์กระจายสินค้า | เมืองของ DC | ทีมจัดส่ง | รถบรรทุก |
|---|---|---|---|---|---|
| 1001 | Keystone | Keystone Phoenix DC | Phoenix | ทีม 12 | 117 |
| 1002 | Keystone | Keystone Phoenix DC | Phoenix | ทีม 8 | 122 |
| 1003 | Keystone | Keystone Phoenix DC | Phoenix | ทีม 12 | 117 |
เมื่อมองแวบแรก ไม่มีอะไรซับซ้อน แต่ดูศูนย์กระจายสินค้า เราซ้ำ “Keystone Phoenix DC, Phoenix” บนทุกการจัดส่งที่ออกจากที่นั่น: Steve Trucking จัดส่งราว 3,300 รายการต่อวันจาก DC เก้าแห่ง จึงเป็นวันละสองสามร้อยรายการจากแต่ละแห่ง จะเกิดอะไรขึ้นถ้า DC นั้นย้าย? เราต้องเปลี่ยนกี่เรกคอร์ด และจะเกิดอะไรขึ้นถ้าพลาดไปหนึ่งเรกคอร์ด? ตอนนี้ข้อมูลบอกว่า DC อยู่ในสองเมืองพร้อมกัน นั่นคือความผิดปกติจากการอัปเดต (update anomaly) ถ้าเราอยากตั้ง DC ใหม่ก่อนการจัดส่งครั้งแรกล่ะ? ถ้าเราลบการจัดส่งรายการสุดท้ายของ DC หนึ่ง: เราเพิ่งลบที่เดียวที่เก็บข้อมูลอะไรก็ตามเกี่ยวกับมันไปหรือเปล่า?
สิ่งเหล่านี้คืออาการต่างๆ ของปัญหาเดียวกัน: เราผสมข้อเท็จจริงเกี่ยวกับสิ่งต่างๆ เข้าด้วยกัน ข้อเท็จจริงของศูนย์กระจายสินค้าควรอยู่กับศูนย์กระจายสินค้า ข้อเท็จจริงของการจัดส่งควรอยู่กับการจัดส่ง ข้อเท็จจริงของทีมควรอยู่กับทีม นั่นคือ normalization ในภาษาของการปฏิบัติ
รูปแบบบรรทัดฐานที่หนึ่ง (First Normal Form): อย่าซ่อนโครงสร้างที่ซ้ำ
หัวข้อที่มีชื่อว่า “รูปแบบบรรทัดฐานที่หนึ่ง (First Normal Form): อย่าซ่อนโครงสร้างที่ซ้ำ ”มาเริ่มจากรูปแบบบรรทัดฐานที่หนึ่ง (First Normal Form) หรือ 1NF ซึ่งเป็นจุดที่บางครั้งคนทำให้ normalization ยากเกินจำเป็น
สมมติว่าการจัดส่งมีฟิลด์ชื่อ Items: “ตู้เย็น, ชุดเครื่องทำน้ำแข็ง, รับขนของเก่า” เราใส่ข้อมูลสามชิ้นลงในฟิลด์เดียว มันดูสะดวก จนกระทั่งมีคนถามว่า “เดือนที่แล้วเรารับขนของเก่าไปกี่ครั้ง?” หรือลองนึกถึงฟิลด์ชื่อ Item 1, Item 2 และ Item 3 มันใช้ได้ จนกระทั่งเจอการจัดส่งที่มีสี่รายการ ปัญหาไม่ใช่ว่าเราเลือกจำนวนฟิลด์ Item ผิด ปัญหาคือเราไม่ได้มองเห็นโครงสร้างที่ซ้ำ การจัดส่งหนึ่งครั้งมีหลายรายการได้ นั่นคือข้อมูลที่เราควรสร้างโมเดล: รายการจัดส่งที่เราพบในโมดูลที่ 2
บทเรียนเชิงปฏิบัติของ 1NF: อย่าซ่อนโครงสร้างที่ซ้ำไว้ในฟิลด์ ถ้าธุรกิจบอกคุณว่าบางอย่างเกิดขึ้นได้หลายครั้ง ให้ฟัง อาจมีอีกเอนทิตีหนึ่งที่กำลังพยายามออกมา
คุณจะเห็นสิ่งนี้ใน org จริงอยู่ตลอด มีคนต้องการคำอธิบายหลายภาษา เขาจึงสร้างฟิลด์ชื่อ Description_English แล้วก็ Description_French แล้วก็ Description_German หรือการอนุมัติต้องใช้คนหลายคน เราจึงได้ Approver 1, Approver 2, Approver 3 เมื่อผมเจอแบบนั้นตอนรีวิว ก็ต้องตีมือแล้วไปแก้โมเดลข้อมูล หนึ่งหรือสองอาจพอได้ ถ้าธุรกิจมีแค่หนึ่งหรือสองจริงๆ ตลอดไป อะไรที่มากกว่านั้นควรเป็นออบเจกต์ลูก: คำแปล (Translation) พร้อมภาษา ขั้นตอนการอนุมัติ (Approval Step) พร้อมผู้อนุมัติ ภาษาถัดไป หรือผู้อนุมัติคนที่สี่ จะกลายเป็นเรกคอร์ดใหม่ ไม่ใช่ฟิลด์ใหม่บวกการ deploy
รูปแบบบรรทัดฐานที่สอง (Second Normal Form): อธิบายสิ่งนั้นทั้งหมด
หัวข้อที่มีชื่อว่า “รูปแบบบรรทัดฐานที่สอง (Second Normal Form): อธิบายสิ่งนั้นทั้งหมด ”รูปแบบบรรทัดฐานที่สอง (Second Normal Form) หรือ 2NF มีความหมายเมื่อเรกคอร์ดถูกระบุด้วยหลายสิ่งที่ผสมกัน ลองดูรายการจัดส่ง มันมี ID ของเรกคอร์ดของตัวเอง แต่แต่ละรายการไม่ซ้ำกันด้วย การจัดส่ง + สินค้า สมมติว่าเราเก็บการจัดส่ง สินค้า จำนวน และคำอธิบายสินค้า
จำนวนสมเหตุสมผลตรงนี้: มันอธิบายสินค้านี้ในการจัดส่งนี้ แต่คำอธิบายสินค้าไม่ได้อธิบายสิ่งที่ผสมกันนั้น มันอธิบายสินค้า ใส่มันไว้บนทุกรายการจัดส่ง แล้วเราก็จะซ้ำมันทุกครั้งที่ตู้เย็นรุ่นนั้นออกไปบนรถบรรทุกอีกคัน นั่นคือเบาะแสว่าข้อเท็จจริงนี้ควรอยู่ที่อื่น
ดังนั้นคำถามของ 2NF คือ: แอตทริบิวต์นี้อธิบายสิ่งที่ผมกำลังระบุทั้งหมด หรือแค่บางส่วน? ถ้ามันอธิบายแค่สินค้า มันก็ไปอยู่กับสินค้า ถ้ามันอธิบายตำแหน่งของสินค้าในการจัดส่งนี้โดยเฉพาะ (จำนวน ติดตั้งหรือไม่) มันก็ควรอยู่บนรายการจัดส่ง ผู้ดูแลระบบหลายคนจะไม่มีวันพูดอย่างรู้ตัวว่า “ตอนนี้ผมกำลังใช้รูปแบบบรรทัดฐานที่สอง” ไม่เป็นไร ผมสนใจมากกว่าที่คุณจะเรียนรู้ที่จะถามว่า: ข้อเท็จจริงนี้อธิบายอะไรจริงๆ?
รูปแบบบรรทัดฐานที่สาม (Third Normal Form): อย่าเก็บข้อเท็จจริงเกี่ยวกับข้อเท็จจริงอื่น
หัวข้อที่มีชื่อว่า “รูปแบบบรรทัดฐานที่สาม (Third Normal Form): อย่าเก็บข้อเท็จจริงเกี่ยวกับข้อเท็จจริงอื่น ”ทีนี้มาถึงข้อที่ผมใส่ใจที่สุดสำหรับการสร้างโมเดลในชีวิตประจำวัน: รูปแบบบรรทัดฐานที่สาม (Third Normal Form) หรือ 3NF
ศูนย์กระจายสินค้าแต่ละแห่งมีผู้จัดการฝั่งพันธมิตรค้าปลีก คนที่ใช้พอร์ทัลพันธมิตร สมมติว่าเราใส่ชื่อผู้จัดการ DC ไว้บนทุกการจัดส่งโดยตรง จะเกิดอะไรขึ้นเมื่อ DC ได้ผู้จัดการคนใหม่? เราต้องอัปเดตทุกการจัดส่งจาก DC นั้น นั่นคือกลิ่นเดิมอีกแล้ว: เรากำลังซ้ำข้อเท็จจริงที่ควรอยู่ที่อื่น การจัดส่งออกจาก DC และ DC มีผู้จัดการ นั่นคือความสัมพันธ์สองชุด และชื่อผู้จัดการควรอยู่กับ DC
ตัวอย่างคลาสสิกในตำราคือที่อยู่: รหัสไปรษณีย์ เมือง และรัฐถูกเก็บรวมกันบนทุกเรกคอร์ด ทั้งที่เมืองและรัฐมักตามมาจากรหัสไปรษณีย์ รูปแบบเดียวกัน คำถามเดียวกัน
คำถามเชิงปฏิบัติของ 3NF คือ: ข้อเท็จจริงนี้อธิบายสิ่งนี้ หรือสิ่งอื่นที่สิ่งนี้สัมพันธ์ด้วย? ผมอยากให้คุณถามมันตลอดเวลา
ทีนี้ถอยออกมา แบบเดียวกับที่เราทำกับสุนัข ตารางการจัดส่งที่เราเริ่มต้นกลายเป็นห้าสิ่ง แต่ละสิ่งเก็บข้อเท็จจริงของตัวเอง: รายการจัดส่งแยกออกมาที่รูปแบบบรรทัดฐานที่หนึ่ง สินค้าแยกออกมาที่ขั้นที่สอง และเมืองกับผู้จัดการของศูนย์กระจายสินค้าย้ายไปอยู่ที่ศูนย์กระจายสินค้าที่ขั้นที่สาม
คำถามสามข้อเดียวกัน ผลลัพธ์เดียวกัน: ข้อเท็จจริงทุกข้ออยู่ในที่เดียว
วิธีง่ายๆ ในการคิดเรื่อง 1NF, 2NF และ 3NF
หัวข้อที่มีชื่อว่า “วิธีง่ายๆ ในการคิดเรื่อง 1NF, 2NF และ 3NF ”คุณใช้เวลาทั้งคอร์สฐานข้อมูลไปกับ normalization ได้ เราจะไม่ทำแบบนั้น สำหรับหลักสูตรนี้ ผมอยากให้คุณมีความเข้าใจในระดับที่สถาปนิกใช้งานได้จริง:
- 1NF: ผมกำลังซ่อนหลายค่าหรือโครงสร้างที่ซ้ำไว้ในฟิลด์หรือเรกคอร์ดหรือเปล่า?
- 2NF: ข้อเท็จจริงนี้อธิบายสิ่งที่ผมกำลังระบุทั้งหมดไหม?
- 3NF: ข้อเท็จจริงนี้อธิบายเอนทิตีนี้จริงๆ หรืออธิบายสิ่งที่สัมพันธ์กับมัน?
สิ่งเหล่านั้นไม่ใช่นิยามสำหรับตำราฐานข้อมูล มันคือคำถามที่ผมใช้ได้จริงขณะออกแบบ
ใช่ normalization ไปได้ไกลกว่านั้น: Boyce-Codd Normal Form (BCNF) ทำให้กฎรอบๆ 3NF เข้มงวดขึ้นสำหรับโมเดลที่มีคีย์ซับซ้อนกว่า และยังมี Fourth, Fifth และแม้แต่ Sixth Normal Form พวกมันจัดการกับการซ้ำที่พบได้ยากกว่า แบบที่มาจากโครงสร้างคีย์ที่ไม่ธรรมดามากกว่าจากเรกคอร์ดธุรกิจในชีวิตประจำวัน ทฤษฎีเยอะมาก และจากประสบการณ์ของผม ไม่คุ้มที่จะปวดหัวกับระบบแบบที่เราสร้างบน Salesforce ในระบบธุรกิจ แรงกดดันมักไปทางตรงกันข้าม: เรามีแนวโน้มจะถอยออกจากรูปแบบบรรทัดฐานที่สาม เพื่อผู้ใช้ การทำรายงาน และประสิทธิภาพ มากกว่าจะไปไกลกว่ามัน นั่นคือเรื่องของโมดูลถัดไป รู้ไว้ว่ารูปแบบที่ไกลกว่านั้นมีอยู่ แต่สำหรับงานส่วนใหญ่ที่เราทำ 1NF ถึง 3NF ให้เครื่องมือทางความคิดที่เราต้องการ
ออกแบบให้ถึงรูปแบบบรรทัดฐานที่สาม
หัวข้อที่มีชื่อว่า “ออกแบบให้ถึงรูปแบบบรรทัดฐานที่สาม ”นี่คือจุดยืนของผม เมื่อผมพยายามเข้าใจธุรกิจและออกแบบโมเดลเชิงตรรกะของมัน ผมอยากเริ่มอย่างสะอาด: ข้อเท็จจริงของการจัดส่งอยู่กับการจัดส่ง ข้อเท็จจริงของศูนย์กระจายสินค้าอยู่กับ DC ข้อเท็จจริงของรถบรรทุกอยู่กับรถบรรทุก แนวคิดที่ซ้ำถูกสร้างโมเดลเป็นแนวคิด ผมไม่อยากให้ข้อเท็จจริงทางธุรกิจถูกคัดลอกไปทั่วโมเดลแค่เพราะมันอาจทำให้หน้าจอหรือรายงานง่ายขึ้น ยังไม่ใช่ตอนนี้
แต่ normalization ไม่ใช่ศาสนา เป้าหมายไม่ใช่โมเดลที่บริสุทธิ์ทางวิชาการที่สุดเท่าที่เป็นไปได้ ผมเคยเห็นสถาปนิกที่จดจ่อกับความบริสุทธิ์จนโมเดลกลายเป็นความเจ็บปวดสำหรับทุกคนที่ต้องใช้มัน เราไม่ได้พยายามชนะการประกวด normalization เรากำลังสร้างระบบ และระบบนั้นมีผู้ใช้ รายงาน การผสานรวม ความต้องการด้านประสิทธิภาพ ขนาด และ AI ที่อ่านข้อมูลมากขึ้นเรื่อยๆ สิ่งเหล่านั้นอาจต้องการข้อมูลในรูปแบบที่ต่างกัน
ดังนั้นจุดเริ่มต้นของผมคือ ออกแบบให้ถึงรูปแบบบรรทัดฐานที่สามเท่าที่ทำได้ แล้วถอยออกมาเมื่อจำเป็น: เข้าใจก่อนว่าข้อเท็จจริงแต่ละข้อควรอยู่ที่ไหน แล้วถ้าเราจะแหกจากมัน ผมอยากรู้ว่าทำไม ถ้าคำตอบคือ “เพราะผมไม่รู้ว่าจะเอาฟิลด์ไปไว้ที่ไหนอีก” นั่นยังไม่ดีพอ ถ้าคำตอบคือ “เราตั้งใจคัดลอกค่านี้เพราะมันทำให้ scorecard ของพันธมิตรเรียบง่าย และเรามีวิธีที่เชื่อถือได้ในการซิงก์ให้ตรงกัน” ตอนนี้เรากำลังคุยเรื่องสถาปัตยกรรมกันแล้ว
สิ่งที่สถาปนิกต้องตัดสินใจในเรื่องนี้
หัวข้อที่มีชื่อว่า “สิ่งที่สถาปนิกต้องตัดสินใจในเรื่องนี้ ”เมื่อผมทำ normalization กับโมเดลเชิงตรรกะ ผมมองหา:
- ค่าหรือโครงสร้างที่ซ้ำซึ่งควรสร้างโมเดลแยกต่างหาก
- แอตทริบิวต์ที่อธิบายเอนทิตีอื่น ไม่ใช่เอนทิตีที่มันถูกเก็บไว้
- ข้อเท็จจริงที่ถูกทำซ้ำโดยไม่จำเป็นไปทั่วหลายเรกคอร์ด และการพึ่งพาที่ทำให้มันไม่สอดคล้องกัน
- แนวคิดทางธุรกิจที่ซ่อนอยู่ในฟิลด์
- โมเดลที่สะอาดซึ่งแทนความจริงก่อนการประนีประนอมใดๆ ในการ implement
ไม่ใช่เพราะระบบเชิงกายภาพทุกระบบต้องเป็นบรรทัดฐานอย่างสมบูรณ์แบบ ตรงกันข้ามเลย ในโมดูลถัดไปเราจะตั้งใจแหกจากโมเดลที่สะอาด เพื่อผู้ใช้ การทำรายงาน ขนาด การวิเคราะห์ และ AI และเราจะทำโดยรู้แน่ชัดว่ากำลังแลกอะไรไป
