PlusMagi's Blog By Pitt Phunsanit data engineering,data management,Data Visualization,Programming SQL: ทำไมและเมื่อไหร่ที่ต้องใช้ start_date >= … AND date_end <= …

SQL: ทำไมและเมื่อไหร่ที่ต้องใช้ start_date >= … AND date_end <= …

ในการออกแบบระบบฐานข้อมูลที่ต้องจัดการกับมิติของเวลา เช่น ระบบจองห้องพัก, สัญญาจ้าง, แผนงานโครงการ หรือแคมเปญโปรโมชัน หนึ่งในเงื่อนไขที่พบบ่อยแต่สร้างความสับสนได้ง่ายที่สุดคือรูปแบบ

SELECT *
FROM projects
WHERE start_date >= '1982-08-05'
  AND date_end <= '3000-12-31';

เงื่อนไขนี้ไม่ได้มีไว้สำหรับการค้นหาข้อมูลตามช่วงเวลาทั่วไป แต่มีเจตนาเฉพาะเจาะจงทางตรรกะ รวมถึงมีพฤติกรรมการทำงานบน Database Engine ที่นักพัฒนาจำเป็นต้องเข้าใจลึกซึ้ง


1. ทำไมถึงต้องใช้เงื่อนไขนี้? (The Core Intent)

เหตุผลเดียวในการใช้เงื่อนไขนี้คือ คุณต้องการข้อมูลที่มี “วงจรชีวิตทั้งหมดบรรจุอยู่ภายในกรอบเวลาอย่างสมบูรณ์” (Fully Contained / Enclosed)

หากกำหนดกรอบการค้นหาเป็นปี 1982 (1982-01-01 ถึง 1982-12-31):

  • รายการที่เริ่มและจบในปี 1982
    ดึงมาแสดง
  • รายการที่เริ่มปี 1982 แต่ลากยาวมาจบปี 2003: ถูกตัดทิ้ง (เพราะ start_date < '1982-01-01')
  • รายการที่เริ่มปลายปี 1982 แต่ไปจบต้นปี 2007: ถูกตัดทิ้ง (เพราะ date_end > '1982-12-31')

เคสการใช้งานจริงที่เหมาะสม

  • โครงการประเภท Short-term
    เช่น การตรวจสอบโปรเจกต์พิเศษที่ต้องเริ่มและส่งมอบภายในปีงบประมาณเดียวกันเท่านั้น
  • สัญญาแบบปิด

2. จุดตัดสำคัญ: ระวังจำสับสนกับ “ช่วงเวลาคาบเกี่ยว” (Overlap)

ข้อผิดพลาดที่พบได้บ่อยที่สุดในโปรดักชัน คือ นักพัฒนาต้องการดึงงานที่ active ในช่วงเวลานั้น แต่กลับไปเขียนแบบ Contained

ถ้าโจทย์ธุรกิจคือ “อยากรู้ว่าในปี 1982 มีงานไหนบ้างที่กำลังดำเนินการอยู่” การใช้ start_date >= ... AND date_end <= ... จะทำให้ข้อมูลงานระยะยาว (เช่น งานที่เริ่มตั้งแต่ปี 1982 และจะเสร็จปี 2082) หายไปจากระบบทันที

สำหรับโจทย์ที่ต้องการดึงงานที่มีความคาบเกี่ยว (Overlap) ทั้งหมด สูตรที่ถูกต้องต้องเป็น

-- ดึงทุกงานที่มีชีวิตอยู่หรือคาบเกี่ยวในปี 1982
WHERE start_date <= '1982-12-31'
  AND date_end >= '1982-01-01';
ความต้องการทางธุรกิจรูปแบบ SQL ที่ต้องใช้
ต้องเริ่มและจบ ภายใน กรอบเท่านั้น (Contained)start_date >= 'min' AND date_end <= 'max'
มีความคาบเกี่ยวหรือ แตะ โดนช่วงเวลานี้ (Overlap)start_date <= 'max' AND date_end >= 'min'

3. กับดักเรื่องค่าว่าง (The NULL Trap)

ในระบบงานจริง ฟิลด์ date_end มักอนุญาตให้เป็น NULL สำหรับรายการที่ยังเปิดอยู่ (Ongoing / Active Tasks) หรือสัญญายังไม่มีกำหนดสิ้นสุด

เมื่อคุณรันคำสั่ง

WHERE start_date >= '1982-01-01'
  AND date_end <= '1982-12-31'

การนำ NULL ไปเปรียบเทียบกับ <= '1982-12-31' จะได้ผลลัพธ์เป็น UNKNOWN (ไม่ใช่ TRUE) เสมอ ส่งผลให้รายการที่เริ่มในปี 1982 แต่ยังไม่กำหนดวันสิ้นสุด หรือยังทำไม่เสร็จ หลุดหายไปจากคิวรีทันที

หากเป้าหมายธุรกิจมองว่ารายการที่ยังไม่จบ ถือว่าอยู่ในขอบเขตการตรวจสอบด้วย ต้องเขียนดักทางเลือกไว้เสมอ

WHERE start_date >= '1982-01-01'
  AND (date_end <= '1982-12-31' OR date_end IS NULL)

4. พฤติกรรมด้าน Performance และการทำงานของ Index

เมื่อดูในเชิงการประมวลผล (Query Execution Plan) รูปแบบ start_date >= ... AND date_end <= ... เป็นการใช้ Range Comparison (>=, <=) กับ 2 คอลัมน์ที่แยกจากกัน ซึ่งสร้างความท้าทายให้กับ B-Tree Index:

  • ขีดจำกัดของ Composite Index
    • แม้คุณจะสร้าง Index CREATE INDEX idx_dates ON projects (start_date, date_end)
    • ตัว Optimizer จะสามารถใช้ Index เพื่อกระโดดหาและสแกนช่วงของคอลัมน์แรก (start_date >= '1982-01-01') ได้อย่างมีประสิทธิภาพ
    • แต่สำหรับคอลัมน์ที่สอง (date_end <= '1982-12-31') ฐานข้อมูลจะไม่สามารถทำ Range Seek ซ้อนกันได้ จะต้องนำค่านั้นมาไล่ตรวจ (Filter) ทีละแถวจากชุดข้อมูลที่ผ่านเกณฑ์แรกมาแล้ว
  • การเลือกระหว่าง Index vs Table Scan:
    • หากช่วงเวลาที่กำหนดมีข้อมูลสัดส่วนมากเกินไป (Low Selectivity) Database Engine มักจะข้ามการใช้ Index แล้วหันไปทำ Full Table Scan แทน เพื่อลดภาระการอ่านข้อมูลแบบ Random Access

5. แนวทางปฏิบัติที่ดี (Best Practices)

  1. ระบุชนิดข้อมูลให้ชัดเจน
    หากคอลัมน์เป็น DATETIME หรือ TIMESTAMP หลีกเลี่ยงการเขียน <= '1982-12-31' เพราะระบบจะแปลงเป็น 00:00:00 อัตโนมัติ ทำให้ข้อมูลระหว่างวันตกหล่น ควรใช้แบบ Half-Open Interval เช่น date_end < '2082-01-01'
  2. สร้าง Check Constraint ป้องกันข้อมูลย้อนศร
    เพื่อป้องกัน Data Inconsistency ควรมี Database Constraint กักไว้เสมอว่า CONSTRAINT chk_dates CHECK (date_end >= start_date)
  3. ตรวจสอบเจตนาของโจทย์ให้แน่ใจ
    ก่อนเคาะคีย์บอร์ดพิมพ์ start_date >= ... AND date_end <= ... ให้ทบทวนเสมอว่าคุณกำลังต้องการงานที่ “เริ่มและจบในกรอบเท่านั้น” จริงหรือไม่