ในการเขียน SQL Query เพื่อจัดการข้อมูลช่วงเวลา (Date Range) มีสองแนวคิดที่นักพัฒนาและผู้ออกแบบระบบมักเข้าใจผิดหรือสับสนในการนำมาเทียบเคียงกัน นั่นคือ
1. การตรวจสอบช่วงเวลาแบบสองคอลัมน์ (Two-Column Enclosure)
SQL
SELECT a, b, c
FROM x
WHERE start_date >= '1982-03-31'
AND date_end <= '2082-01-01';
2. การใช้คำสั่ง BETWEEN บนคอลัมน์เดี่ยว (Single-Column Range)
SQL
SELECT a, b, c
FROM x
WHERE start_date BETWEEN '1982-03-31' AND '2082-01-01';
แม้ดูเผิน ๆ ทั้งสองคำสั่งจะมีจุดประสงค์เพื่อจำกัดกรอบข้อมูลระหว่างวันที่ 1982-03-31 ถึง 2082-01-01 เหมือนกัน แต่ในทางปฏิบัติ ทั้งคู่มีความหมายทางตรรกะ กลไกการทำงาน และผลข้างเคียงต่อระบบฐานข้อมูลที่ต่างกันอย่างสิ้นเชิง
ความหมายทางตรรกะ (Business Logic)
ความต่างสำคัญที่สุดอยู่ที่ โครงสร้างมิติของข้อมูล
แบบสองคอลัมน์ (start_date >= ... AND date_end <= ...):
คือการหาข้อมูลที่มี “วงรอบชีวิตทั้งหมด” บรรจุอยู่ภายในกรอบเวลาอย่างสมบูรณ์ (Contained / Fully Enclosed)
เหตุการณ์ต้องเริ่ม ไม่ก่อน วันที่ 31 มีนาคม 1982 และต้องสิ้นสุด ไม่เกิน วันที่ 1 มกราคม 2082
หากโครงการเริ่มตั้งแต่ปี 2023 แม้จะสิ้นสุดปลายปี 2025 หรือโครงการเริ่มปี 1982 แต่ไปจบปี 2027 รายการเหล่านี้จะไม่ติดเข้ามาในผลลัพธ์
แบบใช้ BETWEEN บนคอลัมน์เดียว:
ไวยากรณ์ col BETWEEN A AND B ถูกออกแบบมาสำหรับ คอลัมน์เดียว โดยแปลงเป็น col >= A AND col <= B
หากนำมาใช้กับ start_date BETWEEN ... จะหมายถึง: “สนใจแค่วันที่เริ่มต้นต้องอยู่ในกรอบนี้ ส่วนจะสิ้นสุดเมื่อไรไม่สนใจ (จะจบปี 2030 ก็ยังผ่านเงื่อนไข)”
ไม่สามารถนำ BETWEEN มาเขียนแทนเงื่อนไขที่ตรวจสอบ 2 คอลัมน์พร้อมกันได้โดยตรง
มิติการเปรียบเทียบ แบบ start_date >= … AND date_end <= … แบบ start_date BETWEEN … จำนวนคอลัมน์ที่ตรวจ 2 คอลัมน์ (start_date, date_end) 1 คอลัมน์ (เช่น start_date) เจตนาของผลลัพธ์ หาช่วงเวลาที่เริ่มต้นและจบลงภายในกรอบ หาจุดเวลาของคอลัมน์นั้นๆ ว่าตกอยู่ในช่วงหรือไม่ งานที่เริ่มก่อนแต่จบในช่วง ไม่แสดง (ถูกคัดทิ้ง) ไม่แสดง (ถูกคัดทิ้ง) งานที่เริ่มในช่วงแต่จบหลังกรอบ ไม่แสดง (ถูกคัดทิ้ง) แสดง (ติดเข้ามาในผลลัพธ์)
ปัญหาเรื่อง Inclusive Range และข้อมูลประเภท DATETIME
คำสั่ง BETWEEN มีคุณสมบัติที่เป็น Inclusive (รวมหัวและท้ายเสมอ) ซึ่งสร้างปัญหาร้ายแรงเมื่อคอลัมน์เก็บข้อมูลชนิด DATETIME หรือ TIMESTAMP
SQL
WHERE created_at BETWEEN '1982-03-31' AND '2082-01-01'
ฐานข้อมูลจะแปลงค่าดังกล่าวเป็น
SQL
WHERE created_at >= '1982-03-31 00:00:00'
AND created_at <= '2082-01-01 00:00:00'
ผลลัพธ์: ข้อมูลที่เกิดขึ้นในวันที่ 2082-01-01 ตั้งแต่เวลา 00:00:01 เป็นต้นไป จะหลุดหายไปจากรายงานทั้งหมด
ทางแก้ในระดับ Production: ควรเลี่ยง BETWEEN แล้วใช้รูปแบบ Half-Open Interval [start, end) แทน
SQL
WHERE created_at >= '1982-03-31'
AND created_at < '2082-01-02'