ข้ามไปยังเนื้อหา

วางข้อมูลในที่ที่ควรอยู่

มีให้อ่านอีก 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)

Two identical drawers on a workbench, seen from above: one jammed with everything mixed together, one divided so each kind of thing has its own compartment.
ภาพสร้างด้วย AI

โมดูลที่ 4 จาก 6 ในบท การสร้างโมเดลข้อมูลเพื่อความหมาย บทที่ 2 ของหลักสูตร Data Architect แต่ละโมดูลอ่านแยกกันได้ ก่อนหน้า: สร้างโมเดลเชิงตรรกะ · ถัดไป: ออกแบบถึงรูปแบบบรรทัดฐานที่สาม แล้วถอยออกมา

คำถามที่โมดูลนี้ตอบ: normalization ช่วยให้ผมรู้ได้อย่างไรว่าข้อมูลควรอยู่ที่ไหนจริงๆ?

เรามีโมเดลเชิงตรรกะแล้ว: เอนทิตี ความสัมพันธ์ แอตทริบิวต์ และคีย์ ตอนนี้ผมอยากถามหนึ่งในคำถามที่สำคัญที่สุดในการสร้างโมเดลข้อมูล: ข้อมูลนี้ควรอยู่ตรงนี้จริงหรือ?

ฟังดูง่าย แต่ไม่ใช่ ผมเห็นระบบมากมายที่ทุกฟิลด์สมเหตุสมผลในตอนที่มีคนสร้างมัน ปัญหาไม่ใช่ว่าฟิลด์เหล่านั้นไร้สาระ แต่คือข้อเท็จจริงถูกเก็บไว้ผิดที่ การจัดส่งเก็บข้อมูลที่จริงๆ แล้วอธิบายศูนย์กระจายสินค้า ที่อยู่เดียวกันปรากฏบนเรกคอร์ดหลายร้อยรายการ ฟิลด์เดียวเก็บสามค่าคั่นด้วยจุลภาค การเปลี่ยนข้อเท็จจริงทางธุรกิจข้อเดียวหมายถึงการอัปเดต 10,000 เรกคอร์ด นั่นคือปัญหาที่ normalization (การทำให้เป็นบรรทัดฐาน) ช่วยเราแก้ และก่อนที่เราจะจมอยู่กับศัพท์ฐานข้อมูล นี่คือแนวคิดทั้งหมด: วางข้อเท็จจริงไว้ในที่ที่มันควรอยู่ ทุกอย่างที่เหลือในโมดูลนี้ช่วยให้เราทำเช่นนั้นได้อย่างเป็นระบบมากขึ้น

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

ทำไมต้องลำบาก? เพราะข้อเท็จจริงที่เก็บครั้งเดียวคือข้อเท็จจริงที่เราเปลี่ยนครั้งเดียว normalization ป้องกันความซ้ำซ้อน (redundancy) คือข้อเท็จจริงเดียวกันที่ถูกคัดลอกไปทั่วหลายเรกคอร์ด และป้องกันความไม่สอดคล้องที่แทรกเข้ามาเมื่อสำเนาหนึ่งถูกอัปเดตแต่สำเนาอื่นไม่ถูกอัปเดต นั่นคือเหตุผลที่มันเหมาะกับระบบธุรกรรมอย่าง Salesforce ที่เรกคอร์ดถูกสร้างและอัปเดตตลอดทั้งวัน

เราทำ normalization เป็นขั้นๆ ที่เรียกว่า รูปแบบบรรทัดฐาน (normal form) และแต่ละขั้นถามคำถามเพียงข้อเดียว:

  • รูปแบบบรรทัดฐานที่หนึ่ง (1NF): หนึ่งค่าต่อหนึ่งฟิลด์ และมีบางอย่างที่ระบุทุกแถวได้อย่างไม่ซ้ำ
  • รูปแบบบรรทัดฐานที่สอง (2NF): ข้อเท็จจริงทุกข้ออธิบายสิ่งที่ระบุแถวนั้นทั้งหมด ไม่ใช่แค่บางส่วน
  • รูปแบบบรรทัดฐานที่สาม (3NF): ข้อเท็จจริงทุกข้ออธิบายสิ่งนี้ ไม่ใช่สิ่งอื่นที่มันสัมพันธ์ด้วย

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

เวลาผมสอนสด ผมไม่ได้เริ่มจากการจัดส่ง ผมเริ่มจากสุนัข ทุกคนเข้าใจสุนัข จึงไม่มีใครถูกเรื่องธุรกิจดึงความสนใจไป และเราได้เห็นข้อมูลพยายามหาบ้านของมัน จากนั้นเราจึงนำวิธีเดียวกันไปใช้กับ Steve Trucking

เริ่มต้น: สเปรดชีตดิบ ผมจะใส่สุนัขสักสองสามตัวขึ้นจอ แล้วคนในห้องก็เพิ่มสุนัขของพวกเขา ภายในไม่กี่นาทีเราก็มีรายการยาวรายการเดียว: สุนัข ปีที่เกิด ใครเป็นเจ้าของ เจ้าของอยู่ที่ไหน สายพันธุ์ ใครเป็นผู้เพาะพันธุ์ และท่าที่มันทำได้

สุนัขปีเกิดเจ้าของรหัสไปรษณีย์เมืองสายพันธุ์ผู้เพาะพันธุ์ท่า
Bella2017Marcus80202Denverลาบราดอร์Maple Ridgeนั่ง, คอย, ยื่นมือ
Milo2019Marcus80202Denverพุดเดิ้ลGolden Meadowนั่ง, กลิ้งตัว
Max2022Ana64105Kansas Cityพันธุ์ผสมนั่ง, แกล้งตาย
Luna2020Kenji98101Seattleฮัสกี้Cascadeนั่ง, คอย, หมุนตัว
Rocky2024Priya30301AtlantaบีเกิลMaple Ridgeนั่ง
Coco2014Ana64105Kansas CityชิวาวาLittle Pawsยื่นมือ, หมุนตัว

มันใช้ได้ สเปรดชีตทุกอันใช้ได้ในวันแรก แต่ดูสิ่งที่ผิดอยู่แล้ว ผมจะหาสุนัขทุกตัวที่ทำท่ายื่นมือได้อย่างไร? รหัสไปรษณีย์ของ Ana อยู่ในสองแถว ของ Marcus ก็เช่นกัน: จะเกิดอะไรขึ้นถ้ามีคนอัปเดตแถวหนึ่งแต่ไม่อัปเดตอีกแถว? Maple Ridge ปรากฏสองครั้ง Kansas City ก็เช่นกัน และ Max มาจากสถานพักพิงสัตว์ จึงไม่มีผู้เพาะพันธุ์เลย

ขั้นที่ 1: รูปแบบบรรทัดฐานที่หนึ่ง กฎสองข้อ แต่ละฟิลด์เก็บค่าเดียว รายการท่าที่คั่นด้วยจุลภาคจึงต้องไป และแต่ละแถวต้องมีบางอย่างที่ระบุมันได้อย่างไม่ซ้ำ ไม่ใช่ชื่อสุนัข มีสุนัขชื่อ Max อยู่เยอะมาก แต่เป็น ID: ID ที่สร้างขึ้นอัตโนมัติหรือ GUID ท่าถูกแยกออกมาเป็นแถวของตัวเอง หนึ่งแถวต่อสุนัขหนึ่งตัวกับท่าหนึ่งท่า และเมื่อท่าหนึ่งมีแถวของตัวเองแล้ว ก็มีที่สำหรับสิ่งที่เราไม่เคยบอกได้ในรายการคั่นจุลภาค: ท่านั้นยากแค่ไหน สุนัขตัวนี้ทำได้ดีแค่ไหน และมันเรียนท่านั้นเมื่อไร

สุนัข (ระบุด้วย ID สุนัข)

ID สุนัขชื่อปีเกิดเจ้าของรหัสไปรษณีย์เมืองสายพันธุ์ผู้เพาะพันธุ์
D-001Bella2017Marcus80202Denverลาบราดอร์Maple Ridge
D-002Milo2019Marcus80202Denverพุดเดิ้ลGolden Meadow
D-003Max2022Ana64105Kansas Cityพันธุ์ผสม
D-004Luna2020Kenji98101Seattleฮัสกี้Cascade
D-005Rocky2024Priya30301AtlantaบีเกิลMaple Ridge
D-006Coco2014Ana64105Kansas 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-001T-01เชี่ยวชาญ2017-11
D-001T-02เชี่ยวชาญ2018-01
D-001T-03ดี2018-06
D-002T-01เชี่ยวชาญ2020-02
D-002T-04กำลังเรียน2023-05
D-003T-01ดี2022-06
D-003T-05กำลังเรียน2024-03
D-004T-01เชี่ยวชาญ2020-11
D-004T-02ดี2021-01
D-004T-06เชี่ยวชาญ2021-06
D-005T-01กำลังเรียน2025-02
D-006T-03เชี่ยวชาญ2015-06
D-006T-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 ก็อยู่ในสองรัฐ เป็นเครื่องเตือนใจที่ดีว่าโลกจริงไม่ได้อ่านตำราของเรา

นั่นคือลำดับขั้น: สเปรดชีตหนึ่งอัน แล้วคำถามสามข้อ และข้อเท็จจริงทุกข้อก็ไปอยู่ในที่เดียว ถอยออกมาดูสิ่งที่เราสร้าง รายการรกๆ หนึ่งรายการกลายเป็นหกตาราง แต่ละตารางอธิบายสิ่งเดียว เชื่อมกันด้วยคีย์:

โมเดลสุนัขหลังรูปแบบบรรทัดฐานที่สาม กล่องหกกล่องเชื่อมด้วยเส้น เจ้าของเป็นเจ้าของสุนัขหนึ่งหรือหลายตัว สุนัขมีเจ้าของหนึ่งคน เป็นสายพันธุ์หนึ่ง และถูกเพาะพันธุ์โดยผู้เพาะพันธุ์ศูนย์หรือหนึ่งราย (ผู้เพาะพันธุ์เพาะพันธุ์สุนัขหนึ่งหรือหลายตัว) ระหว่างสุนัขกับท่าคือท่าของสุนัข: สุนัขทำท่าของสุนัขได้ศูนย์หรือหลายรายการ ท่าปรากฏในท่าของสุนัขศูนย์หรือหลายรายการ และท่าของสุนัขแต่ละรายการเป็นของสุนัขหนึ่งตัวพอดีและท่าหนึ่งท่าพอดี เป็นเจ้าของ เพาะพันธุ์ เป็น ทำได้ ปรากฏใน เจ้าของ ผู้เพาะพันธุ์ สุนัข สายพันธุ์ ท่าของสุนัข ท่า หนึ่งพอดี ศูนย์หรือหนึ่ง หนึ่งหรือหลาย ศูนย์หรือหลาย © 2026 Twinshark LLC · stevetecharc.com
Click to enlarge

ไม่มีใครต้องใช้คำศัพท์ Salesforce สักคำเพื่อตามเรื่องนั้นทัน มันเป็นแค่สุนัข คนรอบตัวพวกมัน และสิ่งที่พวกมันทำได้

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

การจัดส่งพันธมิตรค้าปลีกศูนย์กระจายสินค้าเมืองของ DCทีมจัดส่งรถบรรทุก
1001KeystoneKeystone Phoenix DCPhoenixทีม 12117
1002KeystoneKeystone Phoenix DCPhoenixทีม 8122
1003KeystoneKeystone Phoenix DCPhoenixทีม 12117

เมื่อมองแวบแรก ไม่มีอะไรซับซ้อน แต่ดูศูนย์กระจายสินค้า เราซ้ำ “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

รายการของการจัดส่ง สามแบบ การ์ดสามใบแสดงรายการของการจัดส่งเดียวกันที่เก็บไว้สามแบบ ฟิลด์เดียว: การจัดส่ง 1001 มีฟิลด์ Items ที่เขียนว่า “ตู้เย็น, ชุดเครื่องทำน้ำแข็ง, ขนของเก่าทิ้ง” ซึ่งตอบไม่ได้ว่าเดือนที่แล้วมีการขนของเก่าทิ้งกี่ครั้ง ฟิลด์ซ้ำ: Item 1 ตู้เย็น Item 2 ชุดเครื่องทำน้ำแข็ง Item 3 ขนของเก่าทิ้ง ซึ่งเก็บการจัดส่งที่มีสี่รายการไม่ได้ เรกคอร์ดลูก ซึ่งถูกไฮไลต์: การจัดส่ง 1001 มีรายการจัดส่งสามรายการ คือตู้เย็น ชุดเครื่องทำน้ำแข็ง และขนของเก่าทิ้ง และรายการถัดไปคือเรกคอร์ดใหม่ ไม่ใช่ฟิลด์ใหม่ หมายเหตุด้านล่าง: ถ้าบางอย่างเกิดขึ้นได้หลายครั้ง อาจมีอีกเอนทิตีหนึ่งที่กำลังพยายามออกมา ฟิลด์เดียว สามข้อเท็จจริง ซ่อนในค่าเดียว การจัดส่ง 1001 Items: ตู้เย็น, ชุดเครื่องทำน้ำแข็ง, ขนของเก่าทิ้ง คำถามที่มันตอบไม่ได้ เดือนที่แล้ว ขนของเก่าทิ้งกี่ครั้ง? ฟิลด์ซ้ำ Item 1, Item 2, Item 3 การจัดส่ง 1001 Item 1: ตู้เย็น Item 2: ชุดเครื่องทำน้ำแข็ง Item 3: ขนของเก่าทิ้ง การจัดส่งที่มันเก็บไม่ได้ การจัดส่งที่มี สี่รายการ เรกคอร์ดลูก รายการจัดส่ง สำหรับแต่ละชิ้น การจัดส่ง 1001 รายการจัดส่ง: ตู้เย็น รายการจัดส่ง: ชุดเครื่องทำน้ำแข็ง รายการจัดส่ง: ขนของเก่าทิ้ง รายการถัดไป เรกคอร์ดใหม่ ไม่ใช่ฟิลด์ใหม่ ถ้าบางอย่างเกิดขึ้นได้หลายครั้ง อาจมีอีกเอนทิตีหนึ่งที่กำลังพยายามออกมา © 2026 Twinshark LLC · stevetecharc.com
Click to enlarge

บทเรียนเชิงปฏิบัติของ 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

ตัวอย่างคลาสสิกในตำราคือที่อยู่: รหัสไปรษณีย์ เมือง และรัฐถูกเก็บรวมกันบนทุกเรกคอร์ด ทั้งที่เมืองและรัฐมักตามมาจากรหัสไปรษณีย์ รูปแบบเดียวกัน คำถามเดียวกัน

ข้อเท็จจริงเกี่ยวกับข้อเท็จจริงอื่น: ผู้จัดการ DC สองแผง อยู่บนทุกการจัดส่ง: เรกคอร์ดการจัดส่งที่มี ID ของเรกคอร์ด วันที่จัดส่ง ศูนย์กระจายสินค้า และเมืองกับผู้จัดการของ DC ซึ่งทั้งสองถูกทำเครื่องหมายว่าซ้ำ เก็บครั้งเดียวกับ DC: การจัดส่งเก็บ ID ของเรกคอร์ด วันที่จัดส่ง และ ID ศูนย์กระจายสินค้า และออกจากศูนย์กระจายสินค้า ซึ่งเก็บ ID ของเรกคอร์ด ชื่อ เมือง และผู้จัดการของตัวเอง หมายเหตุด้านล่าง: ผู้จัดการ DC คนใหม่หมายถึงการอัปเดตหลายร้อยครั้งทางซ้าย และครั้งเดียวทางขวา อยู่บนทุกการจัดส่ง เก็บครั้งเดียว กับ DC ออกจาก การจัดส่ง ID การจัดส่ง ID ของเรกคอร์ด วันที่จัดส่ง ศูนย์กระจายสินค้า เมืองของ DC ซ้ำ ผู้จัดการ DC ซ้ำ การจัดส่ง ID การจัดส่ง ID ของเรกคอร์ด วันที่จัดส่ง ID ศูนย์กระจายสินค้า ศูนย์กระจายสินค้า ID ศูนย์กระจายสินค้า ID ของเรกคอร์ด ชื่อ เมือง ผู้จัดการ ผู้จัดการ DC คนใหม่: อัปเดตหลายร้อยครั้งทางซ้าย ครั้งเดียวทางขวา © 2026 Twinshark LLC · stevetecharc.com
Click to enlarge

คำถามเชิงปฏิบัติของ 3NF คือ: ข้อเท็จจริงนี้อธิบายสิ่งนี้ หรือสิ่งอื่นที่สิ่งนี้สัมพันธ์ด้วย? ผมอยากให้คุณถามมันตลอดเวลา

ทีนี้ถอยออกมา แบบเดียวกับที่เราทำกับสุนัข ตารางการจัดส่งที่เราเริ่มต้นกลายเป็นห้าสิ่ง แต่ละสิ่งเก็บข้อเท็จจริงของตัวเอง: รายการจัดส่งแยกออกมาที่รูปแบบบรรทัดฐานที่หนึ่ง สินค้าแยกออกมาที่ขั้นที่สอง และเมืองกับผู้จัดการของศูนย์กระจายสินค้าย้ายไปอยู่ที่ศูนย์กระจายสินค้าที่ขั้นที่สาม

Steve Trucking หลังการทำให้เป็นบรรทัดฐาน กล่องห้ากล่องเชื่อมด้วยเส้น พันธมิตรค้าปลีกดำเนินงานศูนย์กระจายสินค้าศูนย์หรือหลายแห่ง การจัดส่งออกจากศูนย์กระจายสินค้าหนึ่งแห่งพอดี และประกอบด้วยรายการจัดส่งหนึ่งหรือหลายรายการ รายการจัดส่งแต่ละรายการคือสินค้าหนึ่งชนิดพอดี และสินค้าอยู่ในรายการจัดส่งศูนย์หรือหลายรายการ ดำเนินงาน ออกจาก ประกอบด้วย อยู่ใน พันธมิตรค้าปลีก การจัดส่ง รายการจัดส่ง สินค้า ศูนย์กระจายสินค้า หนึ่งพอดี ศูนย์หรือหนึ่ง หนึ่งหรือหลาย ศูนย์หรือหลาย © 2026 Twinshark LLC · stevetecharc.com
Click to enlarge

คำถามสามข้อเดียวกัน ผลลัพธ์เดียวกัน: ข้อเท็จจริงทุกข้ออยู่ในที่เดียว

คุณใช้เวลาทั้งคอร์สฐานข้อมูลไปกับ 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 และเราจะทำโดยรู้แน่ชัดว่ากำลังแลกอะไรไป