บทความนี้จะอธิบายถึงเทคนิคการตัด Column max_age ออกจากตารางเก็บช่วงอายุ เพื่อลดความซ้ำซ้อนของข้อมูล (Data Redundancy) โดยยังคงประสิทธิภาพในการ Query ไว้ครบถ้วน
เทคนิคการลดรูปตาราง: การจัดการช่วงอายุแบบ Dynamic โดยตัด max_age
ในการออกแบบระบบที่ต้องเก็บ “ช่วงอายุ” (Age Range) เช่น ขั้นตอนพัฒนาการของสมอง หรือเกณฑ์ภาษี หลายคนมักออกแบบตารางให้มีทั้ง min_age และ max_age ซึ่งมักจะเจอปัญหา “ข้อมูลขัดแย้งกันเอง” เช่น แถวที่หนึ่งจบที่ 8 แต่แถวที่สองเริ่มที่ 10 (เกิดช่องว่าง) หรือแถวที่สองเริ่มที่ 7 (เกิดช่วงทับซ้อน)
ทางเลือกที่ดีกว่าคือการเก็บเพียง min_age (จุดเริ่มต้น) เพียงอย่างเดียว
ปรับโครงสร้างตารางใหม่ (The Lean Structure)
จากเดิมที่เราต้องคีย์ข้อมูลสองฝั่ง เราจะเหลือเพียงการกำหนด “จุดเปลี่ยน” ของช่วงอายุเท่านั้น
Table: brain_development_stages
| table_id | stage_name | min_age | key_characteristic |
| 1 | Childhood | 0 | Rapid Growth |
| 3 | Adolescence | 9 | High Neuroplasticity |
| 15 | Golden Age | 32 | Peak Stability |
หมายเหตุ: table_id จะกระโดดอย่างไรก็ได้ เพราะเราจะยึด min_age เป็นหลักในการเรียงลำดับ
วิธีการ Query ข้อมูล (The Logic)
เมื่อเราไม่มี max_age ในตาราง เราจะใช้ความสามารถของ Window Function ใน SQL ที่ชื่อว่า LEAD () เพื่อ “มองข้ามไปยังแถวถัดไป” และนำค่า min_age ของแถวถัดไปมาลบออกด้วย 1 เพื่อสร้างเป็น end_range แบบเสมือน
SQL สำหรับสร้าง Dynamic Range
SELECT stage_name, min_age AS start_range, -- มองไปที่แถวถัดไป (เรียงตามอายุ) แล้วลบ 1 ถ้าไม่มีแถวถัดไปให้จบที่ 150 COALESCE (LEAD (min_age) OVER (ORDER BY min_age) - 1, 150) AS end_range, key_characteristic
FROM brain_development_stages;
ทำไมการลดรูปถึงดีกว่า ?
- ป้องกัน Data Inconsistency
คุณจะไม่เจอปัญหา “ช่วงอายุหลุด” หรือ “ช่วงอายุซ้อน” อีกต่อไป เพราะจุดจบของ Stage 1 จะถูกผูกติดกับจุดเริ่มของ Stage 2 โดยอัตโนมัติด้วย Code ไม่ใช่ด้วยการคีย์มือ - แก้ไขข้อมูลได้ง่าย (Single Point of Truth)
หากต้องการเปลี่ยนให้วัยรุ่นเริ่มที่ 10 ปี (แทนที่ 9) คุณแก้แค่จุดเดียวในแถวAdolescenceแล้วระบบจะคำนวณให้เองว่าChildhoodต้องไปจบที่ 9 ทันที - รองรับการแทรกข้อมูล (Scalability)
ถ้าวันหนึ่งคุณอยากเพิ่ม Stage “Pre-Adolescence” ช่วงอายุ 7-8 ปี คุณเพียงแค่INSERTข้อมูลที่มีmin_age = 7เข้าไป โดยไม่ต้องไปไล่ Updatemax_ageของแถวเก่า ๆ ให้เหนื่อย
วิธีการนำไปใช้งานจริง (Search & Join)
เราสามารถใช้โครงสร้างนี้ไป Join กับตาราง Users เพื่อวิเคราะห์ข้อมูลตามช่วงอายุได้ทันทีโดยใช้ BETWEEN
WITH CalculatedStages AS (SELECT *, COALESCE (LEAD (min_age) OVER (ORDER BY min_age) - 1, 150) AS max_age FROM brain_development_stages) SELECT u.name, u.age, s.stage_name
FROM Users u
LEFT JOIN CalculatedStages s ON u.age BETWEEN s.min_age AND s.max_age;
สรุป
การตัด max_age ออก ไม่ใช่การทำให้ข้อมูลหายไป แต่เป็นการเปลี่ยนจากการเก็บข้อมูลที่ “ตายตัวและเสี่ยงต่อการผิดพลาด” ไปเป็นการเก็บข้อมูลแบบ “ความสัมพันธ์ต่อเนื่อง” ซึ่งเป็นแนวคิดที่เหมาะสมอย่างยิ่งสำหรับระบบที่ต้องการความแม่นยำสูงและจัดการง่ายในระยะยาวครับ
อ่านเพิ่มเติม