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

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

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

JavaScript

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) ทั้งหมด สูตรที่ถูกต้องต้องเป็น

JavaScript

-- ดึงทุกงานที่มีชีวิตอยู่หรือคาบเกี่ยวในปี 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) หรือสัญญายังไม่มีกำหนดสิ้นสุด

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

JavaScript

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

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

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

JavaScript

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:


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 <= ... ให้ทบทวนเสมอว่าคุณกำลังต้องการงานที่ “เริ่มและจบในกรอบเท่านั้น” จริงหรือไม่

Exit mobile version