จัดการวันที่ (Date functions)
ประมาณ 30 นาที
เป้าหมายของบท
- ดึงส่วนของวันที่ (ปี/เดือน/วันในสัปดาห์) ด้วย EXTRACT
- ตัดวันที่ให้เป็นหน่วยที่ใหญ่ขึ้น (ต้นเดือน) ด้วย DATE_TRUNC เพื่อเทียบข้ามปีได้ถูกต้อง
- หาระยะห่างระหว่างวันที่ด้วย DATEDIFF
EXTRACT — ดึงส่วนของวันที่
SELECT EXTRACT(year FROM order_date) AS y,
EXTRACT(month FROM order_date) AS m,
EXTRACT(dow FROM order_date) AS dow
FROM orders
dow (day of week) ใน DuckDB ที่ใช้ในเว็บนี้ (และ PostgreSQL): 0 = อาทิตย์ … 6 = เสาร์ — แต่ระบบอื่นอาจนับต่างกัน (เช่น MySQL DAYOFWEEK เริ่มที่ 1 = อาทิตย์) ควรเช็คเอกสารของระบบที่ใช้จริง
DATE_TRUNC — ตัดวันที่ให้เหลือความละเอียดที่กำหนด
SELECT DATE_TRUNC('month', order_date) FROM orders
-- '2024-03-15' กลายเป็น '2024-03-01'
ต่างจาก EXTRACT ตรงที่ผลลัพธ์ยังเป็นวันที่ (ไม่ใช่แค่ตัวเลข) และยังเก็บข้อมูลปีไว้ครบ
นี่คือเหตุผลที่ต้องใช้ DATE_TRUNC('month', ...) แทน EXTRACT(month FROM ...) เฉยๆ เวลาจะเปรียบเทียบ
ยอดขายรายเดือนข้ามปี — ถ้าใช้ EXTRACT(month...) เดือนเดียวกันจากคนละปีจะถูกนับรวมกันโดยไม่ตั้งใจ
เพราะมันดึงแค่ตัวเลขเดือนออกมา ไม่สนใจว่าเป็นปีไหน
DATEDIFF — ระยะห่างระหว่างวันที่
SELECT DATEDIFF('day', signup_date, order_date) AS days_between
FROM ...
นับจาก argument ตัวแรก (start) ไปตัวที่สอง (end) เสมอ — ถ้าสลับตำแหน่งกัน ผลลัพธ์จะได้ค่าเดิม แต่เครื่องหมายกลับกัน (บวกกลายเป็นลบ) ค่าติดลบที่ไม่คาดคิดมักเป็นสัญญาณว่าใส่ argument สลับกัน
เทียบกับ “วันนี้”
ข้อมูลจำลองในเว็บนี้ไม่มี “วันนี้” ที่แท้จริง (ไม่ใช่ข้อมูล real-time) จึงมักใช้วันที่ล่าสุดที่มีอยู่ในระบบ
(MAX(order_date)) เป็นจุดอ้างอิงแทน CURRENT_DATE ในงานจริงที่มีข้อมูล real-time จะใช้ CURRENT_DATE
หรือ NOW() ได้ตรงๆ
ตัวอย่างแก้โจทย์
กด "ขั้นต่อไป" ทีละขั้น ลองรันและแก้โค้ดในแต่ละขั้นได้
ยอดขายรายเดือน — ทำไมต้องใช้ DATE_TRUNC ไม่ใช่ EXTRACT(month) เฉยๆ
อยากดูแนวโน้มยอดขายรายเดือนของปี 2025 เพื่อเทียบกับปีก่อนหน้า
ใช้
EXTRACT(month FROM ...)ดึงเลขเดือนออกมา กรองเฉพาะปี 2025 ด้วยEXTRACT(year FROM ...)
ลูกค้าสมัครมานานกี่วันแล้ว (นับถึงวันล่าสุดที่มีข้อมูลในระบบ)
ข้อมูลจำลองนี้ไม่มี "วันนี้" จริง จึงใช้วันที่ล่าสุดที่มีออเดอร์ในระบบแทน แล้วคำนวณระยะห่างจากวันสมัคร
หา "วันที่ล่าสุด" ในระบบก่อน จะใช้เป็นจุดอ้างอิง
เติมช่องว่าง
แทน ___ ด้วยโค้ดที่ถูกต้อง แล้วกดตรวจคำตอบ
หาจำนวนออเดอร์ในแต่ละวันของสัปดาห์ (0=อาทิตย์ ... 6=เสาร์)
เติม ___ ให้ครบ
แบบฝึกท้ายบท
บทนี้จะนับว่าเสร็จเมื่อผ่านข้อพื้นฐานและประยุกต์ครบ ข้อท้าทายได้ดาวเพิ่ม
🟢 พื้นฐาน
นับจำนวนออเดอร์ในแต่ละปี
นับจำนวนออเดอร์ทั้งหมดที่เกิดในเดือนธันวาคม (ทุกปีรวมกัน)
หาวันที่ล่าสุดที่มีการสั่งซื้อในระบบ
หาจำนวนวันระหว่างออเดอร์แรกสุดกับออเดอร์ล่าสุดในระบบทั้งหมด
🟡 ประยุกต์
ยอดขายรายเดือน (จำนวนออเดอร์) ของปี 2024 เท่านั้น
จำนวนลูกค้าที่สมัครมาแล้วเกิน 500 วัน (นับถึงวันล่าสุดในระบบ)
จำนวนออเดอร์ที่เกิดขึ้นในวันเสาร์หรืออาทิตย์ (weekend)
🔴 ท้าทาย
เดือนไหน (ปีไหน) ที่จำนวนออเดอร์เพิ่มขึ้นจากเดือนก่อนหน้ามากที่สุด (month-over-month, ใช้ LAG)
Quiz
สรุป
- EXTRACT ดึงส่วนของวันที่ออกมาเป็นตัวเลข DATE_TRUNC ตัดวันที่ให้เหลือความละเอียดที่กำหนด
- DATEDIFF(หน่วย, start, end) นับจาก start ไป end เสมอ สลับ argument แล้วเครื่องหมายจะกลับกัน
- เทียบยอดขายรายเดือนข้ามปีต้องใช้ DATE_TRUNC หรือกรองปีด้วย ไม่งั้นเดือนของคนละปีจะถูกรวมกัน
คำถามสัมภาษณ์
จะเปรียบเทียบยอดขายแบบเดือนต่อเดือน (MoM) ข้ามปีให้ถูกต้องต้องทำยังไง
ต้องแยกปีออกจากกันเสมอ ไม่ใช่ GROUP BY แค่เลขเดือนจาก EXTRACT(month...) เพราะจะรวมเดือนเดียวกัน
จากคนละปีเข้าด้วยกันโดยไม่ตั้งใจ วิธีที่ถูกต้องมีสองแบบ: (1) ใช้ DATE_TRUNC('month', date_col)
ซึ่งได้ค่าที่เก็บทั้งปีและเดือนไว้ในตัว (เช่น '2024-03-01') หรือ (2) GROUP BY ทั้ง year และ month แยกกัน
ทั้งสองวิธีทำให้เดือนของแต่ละปีถูกนับแยกกันอย่างถูกต้อง