- 1 1. SQL ที่สั้นที่สุดเพื่อรับวันที่/เวลาในปัจจุบันใน MySQL
- 2 2. ความแตกต่างระหว่าง NOW() / CURRENT_TIMESTAMP / SYSDATE()
- 3 3. การเปลี่ยนรูปแบบการแสดงวันที่/เวลาปัจจุบัน
- 4 4. การคำนวณ datetime โดยใช้เวลาปัจจุบัน
- 4.1 4.1 Add/subtract with INTERVAL
- 4.2 4.2 Get data from the past 24 hours (most common pattern)
- 4.3 4.3 Get day differences with DATEDIFF()
- 4.4 4.4 Use TIMESTAMPDIFF() for hours/minutes/seconds
- 4.5 4.5 Get month start / month end (common real-world trap)
- 4.6 Common mistakes summary
- 4.7 Key takeaways from this section
- 5 5. For range queries, this is safer than BETWEEN
- 6 6. DEFAULT CURRENT_TIMESTAMP และ ON UPDATE (พื้นฐานการออกแบบตาราง)
- 6.1 6.1 ตั้งค่า created_at อัตโนมัติ (DEFAULT CURRENT_TIMESTAMP)
- 6.2 6.2 อัปเดต updated_at อัตโนมัติ (ON UPDATE)
- 6.3 6.3 DATETIME กับ TIMESTAMP (สำคัญ)
- 6.4 6.4 สามารถใช้ CURRENT_TIMESTAMP กับ DATETIME ได้เช่นกัน
- 6.5 6.5 หากต้องการใช้ NOW(): ทางเลือกอื่น (trigger)
- 6.6 ข้อผิดพลาดทั่วไปในการออกแบบ
- 6.7 ประเด็นสำคัญจากส่วนนี้
- 7 7. การออกแบบเขตเวลา (เก็บเป็น UTC, แสดงเป็นเวลาท้องถิ่น)
- 8 8. ตัวอย่างการใช้งานจริงที่คุณสามารถใช้ได้ทันที
- 8.1 8.1 แทรกเวลาปัจจุบันอัตโนมัติลงในบันทึก
- 8.2 8.2 อัปเดตเวลาการล็อกอินล่าสุด
- 8.3 8.3 แก้ไขเวลาอ้างอิงในงานแบบแบทช์
- 8.4 8.4 การรวมข้อมูลสำหรับวันนี้ / เมื่อวาน / 7 วันก่อน
- 8.5 8.5 การตรวจสอบการหมดอายุ (เซสชัน/โทเค็น)
- 8.6 8.6 รับแถวที่หมดอายุภายใน N ชั่วโมง
- 8.7 สิ่งที่คุณต้องจำไว้เสมอ
- 8.8 ประเด็นสำคัญจากส่วนนี้
- 9 9. คำถามที่พบบ่อย (FAQ)
- 10 10. Summary
1. SQL ที่สั้นที่สุดเพื่อรับวันที่/เวลาในปัจจุบันใน MySQL
หากคุณต้องการรับวันที่/เวลาในปัจจุบันใน MySQL ฟังก์ชันแรกที่ควรจำคือ NOW() และ CURRENT_TIMESTAMP ด้านล่างนี้เป็นตัวอย่าง SQL ที่สั้นที่สุดตามกรณีการใช้งาน
1.1 รับวันที่/เวลาในปัจจุบัน (พื้นฐาน)
SELECT NOW();
หรือ
SELECT CURRENT_TIMESTAMP;
- ทั้งสองคืนค่า วันที่/เวลาในปัจจุบัน (YYYY-MM-DD HH:MM:SS)
- ในคิวรีเดียวกัน เวลาเป็นค่าคงที่ที่ “เวลาเริ่มต้นของคิวรี”
กรณีการใช้งาน
- การบันทึกล็อก
- การบันทึกเวลาสร้างเรคคอร์ด
- การอ้างอิงเวลา
ข้อผิดพลาดทั่วไป
- โซนเวลาแอปและโซนเวลาฐานข้อมูลต่างกันทำให้ “เวลาเปลี่ยนแปลง” → ตรวจสอบด้วย
SELECT @@session.time_zone;
1.2 รับวันที่ของวันนี้เท่านั้น
SELECT CURDATE();
- รูปแบบที่คืนค่า:
YYYY-MM-DD - ชนิดคือ DATE (เฉพาะวันที่ ไม่ใช่ datetime)
กรณีการใช้งาน
- ค้นหาข้อมูล “ของวันนี้”
- การสรุปผลประจำวัน
หมายเหตุ
- แตกต่างจาก
NOW()ตรงที่ไม่รวมเวลา - หากต้องการเวลาเพื่อการค้นหาแบบช่วง ให้ใช้
NOW()
1.3 รับเวลาในปัจจุบันเท่านั้น
SELECT CURTIME();
- รูปแบบที่คืนค่า:
HH:MM:SS - ชนิดคือ TIME (เฉพาะเวลา)
กรณีการใช้งาน
- แสดงเวลาการทำงานของแบตช์
- พิมพ์เฉพาะส่วนเวลาในล็อก
ข้อผิดพลาดที่พบบ่อย
- เนื่องจากไม่มีวันที่ จึงไม่สามารถใช้เปรียบเทียบกับ datetime ได้
1.4 รับเวลาใน UTC (สำคัญ)
SELECT UTC_TIMESTAMP();
- คืนค่า UTC ไม่ว่าตามโซนเวลาเซิร์ฟเวอร์ใด
- แนะนำสำหรับบริการระดับโลก
นโยบายพื้นฐานในโครงการจริง
- เก็บเป็น UTC
- แปลงเป็นเวลาในท้องถิ่นเมื่อแสดงผล
หมายเหตุ
- หากแอปของคุณแปลงเป็น UTC แล้ว ต้องระวังการแปลงซ้ำสองครั้ง
1.5 รับมิลลิวินาที / ไมโครวินาที
SELECT NOW(3); -- milliseconds (3 digits after the decimal point)
SELECT NOW(6); -- microseconds (6 digits after the decimal point)
- มีตั้งแต่ MySQL 5.6.4 ขึ้นไป
- ใช้สำหรับล็อกความแม่นยำสูงและการตรวจสอบธุรกรรม
ความเข้าใจผิดทั่วไป
NOW()เพียงอย่างเดียวไม่สามารถคืนค่าทศนิยมของวินาทีได้- คอลัมน์ของคุณต้องระบุความแม่นยำด้วย เช่น
DATETIME(6)
1.6 ฟังก์ชันที่ดีที่สุดตามกรณีการใช้งาน (เริ่มที่นี่หากไม่แน่ใจ)
| Use case | Recommended function |
|---|---|
| General current datetime | NOW() |
| Table default value | CURRENT_TIMESTAMP |
| Date only | CURDATE() |
| Time only | CURTIME() |
| Unified UTC management | UTC_TIMESTAMP() |
| High-precision logs | NOW(6) |
จุดสำคัญที่คนมักพลาด
NOW()และCURRENT_TIMESTAMPมีค่าเท่ากัน (ค่าเดียวกัน)- ในคิวรีเดียวกัน เวลาเป็นค่าคงที่
- ค่าที่แสดงจะแตกต่างตามการตั้งค่าโซนเวลา
- สำหรับไมโครวินาที คำจำกัดความของคอลัมน์ต้องระบุความแม่นยำ
2. ความแตกต่างระหว่าง NOW() / CURRENT_TIMESTAMP / SYSDATE()
มีหลายฟังก์ชันที่ใช้รับเวลาปัจจุบัน แต่ ส่วนที่สับสนที่สุดคือความแตกต่างระหว่าง NOW(), CURRENT_TIMESTAMP และ SYSDATE() หากคุณไม่เข้าใจอย่างถูกต้อง อาจทำให้พฤติกรรมของล็อกและการประมวลผลธุรกรรมไม่คาดคิด
2.1 NOW() และ CURRENT_TIMESTAMP มีค่าเท่ากันโดยพื้นฐาน
SELECT NOW(), CURRENT_TIMESTAMP;
- ทั้งสองคืนค่า datetime ปัจจุบัน (ค่า DATETIME)
- ในคิวรีเดียวกัน เวลาเป็นค่าคงที่ที่ “เวลาเริ่มต้นของคิวรี”
CURRENT_TIMESTAMPยังสามารถใช้แบบเรียกฟังก์ชันได้ด้วย:SELECT CURRENT_TIMESTAMP();
วิธีเลือกใช้ในโครงการจริง
| Use case | Recommended |
|---|---|
| Simple retrieval | NOW() |
| Table DEFAULT / ON UPDATE | CURRENT_TIMESTAMP |
ประเด็นสำคัญ
- ความหมายของค่าที่ดึงมานั้นเหมือนกัน
- ความแตกต่างหลักอยู่ที่ไวยากรณ์/การใช้งาน (เช่น สามารถใช้ใน DEFAULT ได้)
- ไม่ได้เป็นความแตกต่างของชนิดข้อมูล (ง่ายต่อการเข้าใจผิด)
2.2 SYSDATE() คืนค่าเวลาที่ “เวลาประเมินผล”
SYSDATE() ดูคล้ายกับ NOW() แต่ จังหวะที่ค่าถูกตรึงต่างกัน
SELECT NOW(), SLEEP(2), NOW();
ผลลัพธ์ (ตัวอย่าง):
2025-02-23 14:00:00
2025-02-23 14:00:00
SELECT SYSDATE(), SLEEP(2), SYSDATE();
ผลลัพธ์ (ตัวอย่าง):
2025-02-23 14:00:00
2025-02-23 14:00:02
ความแตกต่างที่สำคัญ
| Function | When the time is fixed |
|---|---|
NOW() | Query start time |
SYSDATE() | Evaluation time |
กล่าวคือ SYSDATE() สามารถเปลี่ยนแปลงได้ภายในคิวรีเดียวกันเมื่อเวลาผ่านไป.
2.3 หมายเหตุสำหรับธุรกรรมและการทำซ้ำ
เพราะ NOW() ถูกล็อกไว้ที่จุดเริ่มต้นของ query ดังนั้นมันจึง
ปลอดภัยกว่าสำหรับการอ้างอิงเวลาที่สอดคล้องกันภายในธุรกรรม。
ในทางตรงกันข้าม เพราะ SYSDATE() ขึ้นอยู่กับเวลาการรัน มันสามารถส่งผลต่อความสามารถในการทำซ้ำใน:
- การทำซ้ำ
- งานแบบ batch
- การประมวลผล log จำนวนมาก
กฎง่ายๆ
- ต้องการเวลาอ้างอิงที่คงที่ →
NOW() - ต้องการเวลาที่แน่นอนทุกครั้ง →
SYSDATE()(การใช้งานจำกัด)
2.4 หมายเหตุ: CURRENT_DATE / CURRENT_TIME / LOCALTIME
MySQL ยังรองรับ:
SELECT CURRENT_DATE;
SELECT CURRENT_TIME;
SELECT LOCALTIME;
CURRENT_DATE→ เหมือนกับCURDATE()CURRENT_TIME→ เหมือนกับCURTIME()LOCALTIME→ เหมือนกับNOW()
ความสับสนทั่วไป
- ส่วนใหญ่เป็น “ความแตกต่างทางความงาม”; มีความแตกต่างทางฟังก์ชันน้อยมาก
- เพื่อความปลอดภัย ให้ความสำคัญกับความอ่านง่ายและทำให้มาตรฐานภายในโครงการ
ประเด็นสำคัญจากส่วนนี้
NOW()และCURRENT_TIMESTAMPหมายถึงสิ่งเดียวกันโดยพื้นฐานSYSDATE()มีพฤติกรรมแตกต่างกันเพราะขึ้นอยู่กับเวลาการประเมิน- สำหรับเวลาอ้างอิงที่คงที่ ให้ใช้
NOW()เป็นค่าเริ่มต้น - ใช้
CURRENT_TIMESTAMPสำหรับ DEFAULT และ ON UPDATE
3. การเปลี่ยนรูปแบบการแสดงวันที่/เวลาปัจจุบัน
เวลาปัจจุบันที่ได้จาก NOW() จะแสดงโดยค่าเริ่มต้นเป็น YYYY-MM-DD HH:MM:SS ในการทำงานจริง คุณมักจะต้องการสิ่งเช่น:
- แสดงเฉพาะวันที่
- ใช้
YYYY/MM/DD - ใช้รูปแบบเฉพาะท้องถิ่น (เช่น, Feb 23, 2025)
- แสดงมิลลิวินาที
สำหรับสิ่งนี้ ใช้ DATE_FORMAT() (แปลง datetime เป็นรูปแบบสตริงตามต้องการ)。
3.1 ไวยากรณ์พื้นฐานของ DATE_FORMAT()
DATE_FORMAT(datetime, 'format_string')
ตัวอย่าง: แปลงเวลาปัจจุบันเป็น YYYY/MM/DD HH:MM
SELECT DATE_FORMAT(NOW(), '%Y/%m/%d %H:%i');
ตัวอย่างผลลัพธ์:
2025/02/23 14:35
ตัวระบุรูปแบบทั่วไป
| Specifier | Meaning | Example |
|---|---|---|
%Y | 4-digit year | 2025 |
%m | 2-digit month | 02 |
%d | 2-digit day | 23 |
%H | Hour (24-hour) | 14 |
%i | Minutes | 35 |
%s | Seconds | 50 |
%f | Microseconds | 123456 |
หมายเหตุ
- บางตัวระบุแตกต่างกันระหว่างตัวพิมพ์ใหญ่/เล็ก
%hคือเวลาชั่วโมง 12,%Hคือ 24 ชั่วโมง
3.2 ตัวอย่างรูปแบบทั่วไป (ที่ใช้บ่อยในการทำงานจริง)
1. แยกด้วยเครื่องหมายทับ
SELECT DATE_FORMAT(NOW(), '%Y/%m/%d');
2. เฉพาะท้องถิ่น (ภาษาอังกฤษ)
SELECT DATE_FORMAT(NOW(), '%b %d, %Y');
3. ไม่มีวินาที
SELECT DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i');
หลุมพรางทั่วไป
- เพราะมันคืนค่าสตริง คุณไม่สามารถใช้สำหรับการคำนวณตัวเลข
- สำหรับการเปรียบเทียบและค้นหา ควรใช้ประเภท DATETIME เดิม
3.3 สกัดเฉพาะส่วนวันที่หรือเวลา
คุณยังสามารถดึงส่วนต่างๆ โดยคงประเภทไว้ (แทนที่จะฟอร์แมตเป็นสตริง)
SELECT DATE(NOW()); -- date only (DATE type)
SELECT TIME(NOW()); -- time only (TIME type)
SELECT YEAR(NOW()); -- year only
SELECT MONTH(NOW()); -- month only
SELECT DAY(NOW()); -- day only
การใช้งานที่แนะนำ
| Goal | Recommended |
|---|---|
| Formatting for display | DATE_FORMAT |
| Calculation / comparison | DATE() / TIME() |
3.4 การแสดงด้วยไมโครวินาที
เมื่อคุณต้องการ log ที่มีความแม่นยำสูง:
SELECT NOW(6);
ด้วยการฟอร์แมต:
SELECT DATE_FORMAT(NOW(6), '%Y-%m-%d %H:%i:%s.%f');
หมายเหตุสำคัญ
- คอลัมน์ของคุณต้องเป็น
DATETIME(6)หรือTIMESTAMP(6)มิฉะนั้นความแม่นยำจะไม่ถูกเก็บ - ไม่มีในเวอร์ชัน MySQL ก่อน 5.6.4 (ขึ้นอยู่กับสภาพแวดล้อม)
ข้อผิดพลาดทั่วไป
การใช้
DATE_FORMAT()ใน WHERE clause จะป้องกันไม่ให้ใช้ index wp:list /wp:list- ตัวอย่างไม่ดี:
WHERE DATE_FORMAT(created_at, '%Y-%m-%d') = '2025-02-23' - แนะนำ:
WHERE created_at >= '2025-02-23 00:00:00' AND created_at < '2025-02-24 00:00:00' - การเก็บสตริงที่ฟอร์แมตสำหรับแสดงโดยตรง (ทำร้ายประสิทธิภาพการค้นหา)
- ตัวอย่างไม่ดี:
ประเด็นสำคัญจากส่วนนี้
- ใช้
DATE_FORMAT()เพื่อเปลี่ยนรูปแบบการแสดง - สำหรับการคำนวณ/เปรียบเทียบ ให้คงประเภทเดิมไว้
- หากต้องการไมโครวินาที ให้ระบุความแม่นยำในนิยามคอลัมน์
- การใช้ฟังก์ชันใน WHERE clause สามารถทำให้เกิดปัญหาประสิทธิภาพ
4. การคำนวณ datetime โดยใช้เวลาปัจจุบัน
ใน MySQL การคำนวณสิ่งต่าง ๆ เช่น “N ชั่วโมงถัดไป,” “N วันก่อนหน้า,” หรือ “N วันในอดีต” โดยอิงจากเวลาปัจจุบันนั้นเป็นเรื่องปกติ
กรณีที่พบบ่อยที่สุดในโลกแห่งความเป็นจริงคือ “ดึงข้อมูลจาก 24 ชั่วโมงที่ผ่านมา.”
พื้นฐานของการคำนวณ datetime คือ INTERVAL (ไวยากรณ์สำหรับการบวก/ลบหน่วยเวลา)
4.1 Add/subtract with INTERVAL
Get 1 hour later
SELECT NOW() + INTERVAL 1 HOUR;
Get 3 days ago
SELECT NOW() - INTERVAL 3 DAY;
Get 1 month later
SELECT NOW() + INTERVAL 1 MONTH;
Common units
| Unit | Meaning |
|---|---|
| SECOND | Seconds |
| MINUTE | Minutes |
| HOUR | Hours |
| DAY | Days |
| WEEK | Weeks |
| MONTH | Months |
| YEAR | Years |
Common mistakes
DAYในINTERVAL 1 DAYทำงานได้แม้จะเป็นตัวพิมพ์เล็ก แต่ควรทำให้เป็นมาตรฐานภายในทีม- การคำนวณสิ้นเดือนอาจแตกต่างจากที่คาดหวังเพราะจำนวนวันแตกต่างกัน (เช่น, 31 ม.ค. + 1 เดือน)
4.2 Get data from the past 24 hours (most common pattern)
SELECT *
FROM users
WHERE created_at >= NOW() - INTERVAL 1 DAY;
Meaning
- มุ่งเป้าไปที่ “จากเวลาปัจจุบันย้อนกลับไป 24 ชั่วโมงก่อนหน้า”
Notes
- หากใช้
CURDATE()จุดเริ่มต้นจะกลายเป็น “วันนี้เวลา 00:00” ซึ่งจะเปลี่ยนความหมาย
ตัวอย่าง (ข้อมูลของวันนี้):
SELECT *
FROM users
WHERE created_at >= CURDATE();
Understanding the difference is critical
| Expression | Meaning |
|---|---|
NOW() - INTERVAL 1 DAY | Past 24 hours |
CURDATE() | Since today 00:00 |
4.3 Get day differences with DATEDIFF()
SELECT DATEDIFF('2025-03-01', '2025-02-23');
ผลลัพธ์:
6
- หน่วยคือ “วัน”
- ส่วนของเวลาถูกเพิกเฉย
Notes
- เครื่องหมายจะเปลี่ยนไปตามลำดับอาร์กิวเมนต์
- ไม่สามารถคำนวณความแตกต่างในหน่วยชั่วโมง/นาที/วินาทีได้
4.4 Use TIMESTAMPDIFF() for hours/minutes/seconds
SELECT TIMESTAMPDIFF(HOUR,
'2025-02-23 12:00:00',
'2025-02-23 18:30:00');
ผลลัพธ์:
6
Minutes
SELECT TIMESTAMPDIFF(MINUTE,
'2025-02-23 12:00:00',
'2025-02-23 12:30:00');
Seconds
SELECT TIMESTAMPDIFF(SECOND,
'2025-02-23 12:00:00',
'2025-02-23 12:00:45');
Use cases
- การคำนวณระยะเวลาของเซสชัน
- การตรวจสอบการหมดอายุ
- การตัดสินใจเกี่ยวกับการหมดเวลา
4.5 Get month start / month end (common real-world trap)
ดึงวันแรกของเดือนนี้:
SELECT DATE_FORMAT(NOW(), '%Y-%m-01');
วันแรกของเดือนถัดไป:
SELECT DATE_FORMAT(NOW() + INTERVAL 1 MONTH, '%Y-%m-01');
Notes
- สิ้นเดือนไม่ใช่วันที่คงที่
- สำหรับการคิวรีช่วง “>= เริ่มต้น AND < วันแรกของเดือนถัดไป” เป็นวิธีที่ปลอดภัย
Common mistakes summary
- การใช้
BETWEENกับค่าวันที่อย่างเดียวและลืมส่วนของเวลา - การปิดการใช้งานดัชนีโดยใช้
DATE(created_at)ใน WHERE clauses - การติดกับปัญหา “วันที่ 31” ในการคำนวณเดือน
- การเข้าใจผิดเกี่ยวกับความแตกต่างระหว่าง
NOW()และCURDATE()
Key takeaways from this section
INTERVALคือพื้นฐานของการคำนวณ datetime- 24 ชั่วโมงที่ผ่านมา =
NOW() - INTERVAL 1 DAY - วัน =
DATEDIFF(); หน่วยที่เล็กลง =TIMESTAMPDIFF() - ระวังกับเงื่อนไขสิ้นสุดสำหรับการคำนวณที่อิงเดือน
5. For range queries, this is safer than BETWEEN
เมื่อทำการค้นหาช่วงวันที่ใน MySQL ผู้เริ่มต้นหลายคนใช้ BETWEEN อย่างไรก็ตาม มันมักจะผลิตผลลัพธ์ที่ไม่คาดคิดหากจัดการขอบเขตเวลาไม่ถูกต้อง ดังนั้นจึงแนะนำรูปแบบที่ปลอดภัยกว่าในงานจริง
5.1 BETWEEN basics and the pitfall
A common query
SELECT *
FROM orders
WHERE order_date BETWEEN '2025-02-01' AND '2025-02-10';
ดูเหมือนจะดี แต่ภายในแล้วมันเทียบเท่ากับ:
WHERE order_date >= '2025-02-01 00:00:00'
AND order_date <= '2025-02-10 00:00:00'
ดังนั้น มันไม่รวมข้อมูลหลัง 00:00 ของวันที่ 10 ก.พ.
5.2 Safer pattern (recommended)
Recommended pattern
SELECT *
FROM orders
WHERE order_date >= '2025-02-01 00:00:00'
AND order_date < '2025-02-11 00:00:00';
Key points
- ระบุขอบเขตสิ้นสุดเป็น “น้อยกว่าวันถัดไปเวลา 00:00”
- ปลอดภัยกว่าการใช้
<= 23:59:59(รองรับไมโครวินาที)
5.3 วิธีที่ถูกต้องในการดึงข้อมูลของวันนี้
ตัวอย่างที่ไม่ดี:
WHERE DATE(created_at) = CURDATE();
ปัญหา:
- การใช้ฟังก์ชันกับคอลัมน์ ทำให้ดัชนีไม่ทำงาน
- อาจทำให้การทำงานช้าลงอย่างมากในตารางขนาดใหญ่
แนะนำ:
WHERE created_at >= CURDATE()
AND created_at < CURDATE() + INTERVAL 1 DAY;
นี่ทำให้แน่ใจว่า:
- สามารถใช้ดัชนีได้
- การค้นหาเร็ว
- ไม่มีปัญหาเรื่องขอบเขต
5.4 รูปแบบที่ปลอดภัยสำหรับ 7 วันที่ผ่านมา / 30 วันที่ผ่านมา
7 วันที่ผ่านมา
WHERE created_at >= NOW() - INTERVAL 7 DAY;
30 วันที่ผ่านมา
WHERE created_at >= NOW() - INTERVAL 30 DAY;
หมายเหตุ
CURDATE() - INTERVAL 7 DAYอ้างอิงจาก “วันนี้ 00:00”NOW() - INTERVAL 7 DAYอ้างอิงจาก “เวลาปัจจุบัน”- เลือกใช้ตามความต้องการ
5.5 กฎสำคัญเพื่อให้ดัชนีทำงานได้อย่างมีประสิทธิภาพ
ไม่ดี:
WHERE DATE(created_at) = '2025-02-23';
ดี:
WHERE created_at >= '2025-02-23 00:00:00'
AND created_at < '2025-02-24 00:00:00';
ทำไม:
- ดัชนีทำงานบน “คอลัมน์เอง”
- การใช้ฟังก์ชันทำให้ไม่สามารถใช้ดัชนีได้ (สแกนเต็ม)
สรุปข้อผิดพลาดทั่วไป
- ลืมใส่เวลาในขอบเขตสิ้นสุดของ
BETWEEN - พลาดไมโครวินาทีเมื่อใช้
23:59:59 - ใช้
DATE()ในเงื่อนไข WHERE ทำให้คิวรีช้า - ทำให้ “now” กับ “today 00:00” ไม่ชัดเจน
ประเด็นสำคัญจากส่วนนี้
- คำสั่งค้นหาช่วงช่วงจะปลอดภัยที่สุดเมื่อใช้
>= start AND < end BETWEENต้องจัดการขอบเขตอย่างระมัดระวัง- เพื่อให้ดัชนีทำงานได้อย่างมีประสิทธิภาพ อย่าห่อคอลัมน์ด้วยฟังก์ชัน
- ควรเลือกใช้ตรรกะที่อิง
NOW()หรือCURDATE()อย่างชัดเจน
6. DEFAULT CURRENT_TIMESTAMP และ ON UPDATE (พื้นฐานการออกแบบตาราง)
ในการออกแบบฐานข้อมูล มักจะ จัดการ created_at และ updated_at โดยอัตโนมัติ
ใน MySQL, CURRENT_TIMESTAMP ช่วยให้ตั้งค่าเวลาปัจจุบันโดยอัตโนมัติเมื่อแทรกหรืออัปเดต.
6.1 ตั้งค่า created_at อัตโนมัติ (DEFAULT CURRENT_TIMESTAMP)
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
พฤติกรรม:
INSERT INTO users (name) VALUES ('Alice');
→ เวลาปัจจุบันจะถูกแทรกโดยอัตโนมัติลงใน created_at.
ประเด็นสำคัญ
- สามารถใช้
CURRENT_TIMESTAMPใน DEFAULT ได้ - ไม่สามารถใช้
NOW()โดยตรงใน DEFAULT.
ข้อผิดพลาดทั่วไป
created_at DATETIME DEFAULT NOW(); -- error
เหตุผล:
- ใน MySQL โดยทั่วไปไม่สามารถตั้งฟังก์ชันเป็น DEFAULT ได้ (ยกเว้น
CURRENT_TIMESTAMP).
6.2 อัปเดต updated_at อัตโนมัติ (ON UPDATE)
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP
);
พฤติกรรม:
UPDATE users SET name = 'Bob' WHERE id = 1;
→ updated_at จะอัปเดตเป็นเวลาปัจจุบันโดยอัตโนมัติ.
กรณีการใช้งาน
- ติดตามการเข้าสู่ระบบครั้งล่าสุด
- ประวัติการอัปเดต
- บันทึกการตรวจสอบ
6.3 DATETIME กับ TIMESTAMP (สำคัญ)
| Type | Range | Time zone impact |
|---|---|---|
| DATETIME | Year 1000 to 9999 | No |
| TIMESTAMP | 1970 to 2038 | Yes |
ความแตกต่างที่สำคัญ
TIMESTAMPถูกเก็บภายในเป็น UTC และแปลงเป็นโซนเวลาของเซสชันเมื่อแสดงผลDATETIMEเก็บค่าตามที่ระบุ (ไม่มีการแปลง).
แนวทาง
| Case | Recommended type |
|---|---|
| Log management | TIMESTAMP |
| Future dates (after 2038) | DATETIME |
| Fixed values not requiring TZ conversion | DATETIME |
6.4 สามารถใช้ CURRENT_TIMESTAMP กับ DATETIME ได้เช่นกัน
ใน MySQL 5.6 ขึ้นไป สามารถระบุ CURRENT_TIMESTAMP เป็น DEFAULT และ ON UPDATE สำหรับคอลัมน์ DATETIME ได้เช่นกัน.
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
หมายเหตุ
- เวอร์ชัน MySQL เก่ามีข้อจำกัด (ขึ้นกับสภาพแวดล้อม)
- หากต้องการความแม่นยำ:
DATETIME(6) DEFAULT CURRENT_TIMESTAMP(6)
6.5 หากต้องการใช้ NOW(): ทางเลือกอื่น (trigger)
CREATE TRIGGER set_created_at
BEFORE INSERT ON logs
FOR EACH ROW
SET NEW.created_at = NOW();
กรณีการใช้งาน
- ตรรกะค่าเริ่มต้นที่ซับซ้อน
- การกำหนดค่า timestamp อย่างมีเงื่อนไข
หมายเหตุ
- Trigger ยากต่อการดีบัก
- หลีกเลี่ยงเว้นแต่จำเป็น
ข้อผิดพลาดทั่วไปในการออกแบบ
- เพิ่ม ON UPDATE ให้กับ
created_atและupdated_atทั้งสอง - สับสนระหว่างพฤติกรรมของ DATETIME และ TIMESTAMP
- เลื่อนการตัดสินใจเรื่องเขตเวลา
ประเด็นสำคัญจากส่วนนี้
- ใช้
CURRENT_TIMESTAMPสำหรับ DEFAULT - ใช้
ON UPDATE CURRENT_TIMESTAMPเพื่อติดตามการอัปเดต - เลือกประเภทข้อมูลตามพฤติกรรมของเขตเวลาและปัญหา 2038
- หากต้องการความแม่นยำ อย่าลืม
(6)
7. การออกแบบเขตเวลา (เก็บเป็น UTC, แสดงเป็นเวลาท้องถิ่น)
เมื่อทำงานกับเวลาปัจจุบัน, การออกแบบเขตเวลาที่ไม่ดีทำให้เกิดการลอยของเวลาและการแปลงซ้ำสองครั้ง
ในการปฏิบัติ, ค่าตั้งต้นที่ปลอดภัยที่สุดคือ “เก็บเป็น UTC, แปลงเป็นเวลาท้องถิ่นเพื่อแสดง”

Figure: สถาปัตยกรรมพื้นฐานสำหรับการเก็บ UTC ใน MySQL และการแสดงเวลาท้องถิ่น
7.1 ตรวจสอบเขตเวลาปัจจุบัน
ก่อนอื่น, ตรวจสอบว่า MySQL ทำงานอยู่ในเขตเวลาใด
SELECT @@global.time_zone, @@session.time_zone;
@@global.time_zone→ การตั้งค่าทั่วเซิร์ฟเวอร์@@session.time_zone→ การตั้งค่าการเชื่อมต่อ/เซสชันปัจจุบัน- หากแสดง
SYSTEMจะขึ้นอยู่กับการตั้งค่าของ OS
ปัญหาทั่วไป
- เซิร์ฟเวอร์การผลิตและการพัฒนามีเขตเวลา OS ที่แตกต่างกัน
- แอปแปลงเป็น UTC แล้วฐานข้อมูลแปลงอีกครั้ง (การแปลงซ้ำสองครั้ง)
7.2 เปลี่ยนเขตเวลาในแต่ละเซสชัน
SET time_zone = 'Asia/Tokyo';
or unify to UTC:
SET time_zone = '+00:00';
จุดสำคัญ
- จะกลับสู่ค่าเดิมเมื่อการเชื่อมต่อปิด
- หากใช้ connection pooling, ตรวจสอบการตั้งค่าแอปพลิเคชันของคุณ
7.3 ทำไมต้องเก็บเป็น UTC (หลักการในโลกจริง)
นโยบายที่แนะนำ
- เก็บข้อมูลเป็น UTC
- แปลงเป็นเขตเวลาของผู้ใช้เมื่อแสดงผล
เหตุผล:
- รองรับระดับสากลได้ง่ายขึ้น
- หลีกเลี่ยงปัญหาเวลาออมแสง (DST)
- ลดผลกระทบเมื่อย้ายเซิร์ฟเวอร์
Get UTC:
SELECT UTC_TIMESTAMP();
7.4 แปลงเขตเวลาด้วย CONVERT_TZ()
Example: UTC → JST
SELECT CONVERT_TZ('2025-02-23 05:35:00', '+00:00', '+09:00');
Using time zone names:
SELECT CONVERT_TZ('2025-02-23 05:35:00', 'UTC', 'Asia/Tokyo');
ตัวอย่างในโลกจริง
SELECT CONVERT_TZ(created_at, 'UTC', 'Asia/Tokyo')
FROM users;
7.5 ทำไม CONVERT_TZ() คืนค่า NULL
If it returns NULL, common causes include:
- ตารางเขตเวลา MySQL ยังไม่ได้โหลด
- ชื่อเขตเวลาที่ระบุไม่มีอยู่
Example load on Linux:
mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql
หมายเหตุ
- ในการผลิต, ยืนยันสิทธิ์และว่าต้องรีสตาร์ทหรือไม่
- ใน Docker, zoneinfo อาจไม่ได้รวมอยู่ขึ้นอยู่กับอิมเมจ
7.6 เปลี่ยนเขตเวลาแบบเซิร์ฟเวอร์ทั้งหมด
Config file (my.cnf / my.ini):
[mysqld]
default_time_zone = '+00:00'
Verify after restart:
SELECT @@global.time_zone;
สำคัญ
- สำหรับการเปลี่ยนแปลงในการผลิต, ยืนยันขอบเขตของผลกระทบ
- ระมัดระวังข้อมูลที่มีอยู่ (บางครั้งอาจเป็นการเปลี่ยนแค่การแสดงผล)
ความผิดพลาดทั่วไป
- เก็บเวลาในท้องถิ่นแทน UTC แล้วเพิ่มการสนับสนุนระดับสากลภายหลัง
- การแปลงซ้ำสองครั้งระหว่างแอปและ DB
- ออกแบบด้วย DATETIME โดยไม่คำนึงถึงเขตเวลา
- การตั้งค่า TZ ที่แตกต่างระหว่างการทดสอบและการผลิต
ประเด็นสำคัญจากส่วนนี้
- เก็บเป็น UTC และแปลงเมื่อแสดงผล
- ใช้
UTC_TIMESTAMP() - เมื่อใช้
CONVERT_TZ()ให้ตรวจสอบตาราง TZ - ควรคำนึงถึงความแตกต่างของสภาพแวดล้อมในเขตเวลาเสมอ
8. ตัวอย่างการใช้งานจริงที่คุณสามารถใช้ได้ทันที
นี่คือตัวอย่าง SQL ที่เป็นรูปธรรมแสดง วิธีใช้เวลาปัจจุบันของ MySQL ในการพัฒนา/ปฏิบัติการจริง
เขียนไว้เพื่อให้คุณคัดลอกและวางได้โดยตรง
8.1 แทรกเวลาปัจจุบันอัตโนมัติลงในบันทึก
สร้างตาราง
CREATE TABLE system_logs (
id INT AUTO_INCREMENT PRIMARY KEY,
level VARCHAR(50),
message TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
บันทึก log
INSERT INTO system_logs (level, message)
VALUES ('ERROR', 'Failed to connect to the DB');
ประเด็นสำคัญ
created_atถูกแทรกโดยอัตโนมัติ- ไม่จำเป็นต้องส่ง timestamp จากแอป
- การออกแบบที่จำเป็นสำหรับการตรวจสอบและการสอบสวนเหตุการณ์
ข้อผิดพลาดทั่วไป
- จัดการเวลาทั้งในแอปและฐานข้อมูล
- ความไม่สอดคล้องของเวลาจากการไม่ใช้โซนเวลาที่เป็นเอกภาพ
8.2 อัปเดตเวลาการล็อกอินล่าสุด
การออกแบบตาราง
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255),
last_login TIMESTAMP DEFAULT CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP
);
อัปเดตเมื่อล็อกอิน
UPDATE users
SET last_login = NOW()
WHERE id = 1;
หมายเหตุ
ON UPDATEทำงานเฉพาะเมื่อคอลัมน์เปลี่ยนแปลง- คำสั่ง UPDATE บางคำสั่งอาจไม่เปลี่ยนค่าและจึงไม่มีการอัปเดต
8.3 แก้ไขเวลาอ้างอิงในงานแบบแบทช์
สำหรับกระบวนการที่ทำงานนาน การแก้ไขเวลาอ้างอิงจะปลอดภัยกว่า
SET @base_time = NOW();
SELECT *
FROM orders
WHERE created_at >= @base_time - INTERVAL 1 DAY;
เหตุผล
- เวลาไม่เลื่อนกลางกระบวนการ
- ความสอดคล้องถูกคงไว้
8.4 การรวมข้อมูลสำหรับวันนี้ / เมื่อวาน / 7 วันก่อน
ยอดขายวันนี้
SELECT SUM(amount)
FROM orders
WHERE created_at >= CURDATE()
AND created_at < CURDATE() + INTERVAL 1 DAY;
ยอดขายเมื่อวาน
SELECT SUM(amount)
FROM orders
WHERE created_at >= CURDATE() - INTERVAL 1 DAY
AND created_at < CURDATE();
7 วันก่อน
SELECT COUNT(*)
FROM users
WHERE created_at >= NOW() - INTERVAL 7 DAY;
สำคัญ
- อย่าใช้ฟังก์ชันกับคอลัมน์ใน WHERE clauses
- เขียนเงื่อนไขที่รักษาให้ดัชนีใช้งานได้
8.5 การตรวจสอบการหมดอายุ (เซสชัน/โทเค็น)
SELECT *
FROM sessions
WHERE expires_at > NOW();
ลบแถวที่หมดอายุ
DELETE FROM sessions
WHERE expires_at <= NOW();
ข้อผิดพลาดทั่วไป
- ใช้
CURDATE()และละเลยเวลา - เก็บ UTC แต่เปรียบเทียบกับ
NOW()ท้องถิ่น
8.6 รับแถวที่หมดอายุภายใน N ชั่วโมง
SELECT *
FROM coupons
WHERE expires_at <= NOW() + INTERVAL 1 HOUR;
กรณีใช้งาน:
- การแจ้งเตือนการหมดอายุ
- การแจ้งการหมดอายุ
สิ่งที่คุณต้องจำไว้เสมอ
- ระบุชัดเจนว่าจุดเริ่มต้นของคุณคือ “ตอนนี้” หรือ “วันนี้ 00:00”
- เขียน predicates ที่รักษาดัชนีให้มีประสิทธิภาพ
- ตัดสินใจออกแบบโซนเวลาล่วงหน้า
- อย่าผสม
NOW()และUTC_TIMESTAMP()โดยไม่มีนโยบายที่ชัดเจน
ประเด็นสำคัญจากส่วนนี้
- สำหรับ log/การตรวจสอบ/การติดตามการอัปเดต ใช้
CURRENT_TIMESTAMP - สำหรับการรวมข้อมูล ใช้รูปแบบ
>= AND <ที่ปลอดภัย - สำหรับการจัดการเซสชัน เปรียบเทียบกับ
NOW() - แก้ไขเวลาจุดเริ่มต้นเพื่อให้การประมวลผลสอดคล้อง
9. คำถามที่พบบ่อย (FAQ)
นี่คือคำถามทั่วไปเกี่ยวกับเวลาปัจจุบันใน MySQL โดยเฉพาะหลุมพรางที่เกิดขึ้นในการทำงานจริง
9.1 NOW() และ CURRENT_TIMESTAMP ต่างกันอย่างไร?
สรุป: ความหมายของค่าที่ส่งคืนเหมือนกัน
SELECT NOW(), CURRENT_TIMESTAMP;
ทั้งคู่ส่งคืน datetime ปัจจุบัน
ความแตกต่างหลักคือการใช้งานทางไวยากรณ์
CURRENT_TIMESTAMPสามารถใช้ในDEFAULTและON UPDATENOW()ไม่สามารถใช้โดยตรงใน DEFAULT
หมายเหตุ
- ไม่ใช่ความแตกต่างของประเภท
- คุณสามารถระบุความแม่นยำด้วยอาร์กิวเมนต์เช่น
(6)
9.2 ควรใช้ SYSDATE() หรือไม่?
SYSDATE() ส่งคืนเวลาที่ “เวลาประเมิน”
SELECT SYSDATE(), SLEEP(2), SYSDATE();
ค่าอาจเปลี่ยนแปลงแม้ใน query เดียวกัน
เมื่อใดควรใช้
- เมื่อคุณต้องการบันทึกเวลาจริงที่แน่นอน
เมื่อใดควรหลีกเลี่ยง
- การทำซ้ำ (Replication)
- การประมวลผลธุรกรรมที่ความสอดคล้องสำคัญ
ในกรณีส่วนใหญ่ การใช้ NOW() ก็เพียงพอ
9.3 ทำไมเวลาถึงเลื่อน?
สาเหตุหลัก:
- การตั้งค่าโซนเวลาเซิร์ฟเวอร์
- การตั้งค่าโซนเวลาเซสชัน
- การแปลงซ้ำสองครั้งกับแอปพลิเคชัน
- พฤติกรรมการแปลงอัตโนมัติของประเภท TIMESTAMP
ตรวจสอบด้วย:
SELECT @@global.time_zone, @@session.time_zone;
การบรรเทา
- เก็บเป็น UTC โดยค่าเริ่มต้น
- แปลงเฉพาะเมื่อแสดงผล
- ทำให้แนวนโยบายระหว่างแอปและฐานข้อมูลสอดคล้องกัน
9.4 CONVERT_TZ() returns NULL
สาเหตุ:
- ตารางโซนเวลาไม่ได้โหลด
- ชื่อโซนเวลาไม่ถูกต้อง
วิธีแก้:
mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql
ระมัดระวังเป็นพิเศษในสภาพแวดล้อม Docker.
9.5 Range shifts with BETWEEN
ไม่ดี:
WHERE created_at BETWEEN '2025-02-01' AND '2025-02-10';
ปลอดภัย:
WHERE created_at >= '2025-02-01 00:00:00'
AND created_at < '2025-02-11 00:00:00';
เหตุผล:
- ปัญหาเวลาขอบเขตสุดท้าย
- การรั่วไหลของไมโครวินาที
- ประสิทธิภาพของดัชนี
9.6 How do you choose between DATETIME and TIMESTAMP?
- สนับสนุนระดับสากล / การจัดการ UTC → TIMESTAMP
- หลังปี 2038 หรือวันที่/เวลาแบบคงที่ → DATETIME
ตัดสินใจเรื่องนี้ในขั้นตอนการออกแบบ.
9.7 Microseconds aren’t being stored
สาเหตุ:
- ไม่ได้ระบุความแม่นยำในคำนิยามคอลัมน์
วิธีแก้:
created_at DATETIME(6)
ประเด็นสำคัญจากส่วนนี้
- เริ่มต้นด้วย
NOW()และCURRENT_TIMESTAMP - สำหรับการค้นหาช่วง, ใช้
>= AND < - การเก็บเป็น UTC เป็นค่าเริ่มต้นที่ปลอดภัยที่สุด
- อย่าลืมเลือกประเภทและความแม่นยำ
10. Summary
บทความนี้จัดระเบียบวิธีการเชิงปฏิบัติเพื่อดึงข้อมูล, ฟอร์แมต, คำนวณ, และจัดการเวลาปัจจุบันใน MySQL.
สุดท้าย, นี่คือ สิ่งจำเป็นขั้นพื้นฐานที่คุณควรรู้เพื่อความปลอดภัย.
10.1 Getting the current time (basics)
- การดึงทั่วไป →
NOW() - ค่า DEFAULT ของตาราง / การติดตามการอัปเดต →
CURRENT_TIMESTAMP - เฉพาะวันที่ →
CURDATE() - เฉพาะเวลา →
CURTIME() - UTC →
UTC_TIMESTAMP() - ความแม่นยำสูง →
NOW(6)
กฎโดยประมาณ
- หากไม่แน่ใจ, การใช้
NOW()ก็เพียงพอในหลายกรณี. - สำหรับการออกแบบสคีม่า, ใช้
CURRENT_TIMESTAMP.
10.2 Range queries: “>= AND <” is the golden rule
รูปแบบปลอดภัย:
WHERE created_at >= 'START'
AND created_at < 'END'
ทำไม:
- ป้องกันแถวที่ขาดขอบเขตสุดท้าย
- ทำงานกับไมโครวินาที
- ทำให้ดัชนียังคงใช้งานได้
ตัวอย่างไม่ดี
WHERE DATE(created_at) = CURDATE();
อย่าห่อคอลัมน์ด้วยฟังก์ชัน.
10.3 Datetime arithmetic basics
- บวก/ลบ →
INTERVAL - ความแตกต่างเป็นวัน →
DATEDIFF() - ความแตกต่างเป็นเวลา →
TIMESTAMPDIFF()
ควรคำนึงเสมอว่าจุดอ้างอิงของคุณคือ “now” หรือ “today 00:00.”
10.4 Time zone design principles
- เก็บเป็น UTC
- แปลงเมื่อแสดงผล
- ทำให้แนวนโยบายระหว่างแอปและฐานข้อมูลสอดคล้องกัน
ตรวจสอบด้วย:
SELECT @@global.time_zone, @@session.time_zone;
10.5 Table design best practices
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP
- อุปกรณ์มาตรฐานสำหรับการตรวจสอบ/บันทึก/ติดตามการอัปเดต
- หากต้องการความแม่นยำ, ระบุ
(6)
The 4 most important real‑world points
- อย่าเข้าใจผิดความแตกต่างระหว่าง
NOW()และCURRENT_TIMESTAMP - สำหรับการค้นหาช่วง, ใช้
>= AND < - เก็บเป็น UTC โดยค่าเริ่มต้น
- ตัดสินใจประเภท (DATETIME vs TIMESTAMP) ในขั้นตอนการออกแบบ
การจัดการเวลาปัจจุบันของ MySQL เป็นพื้นฐานสำหรับการบันทึก, การสรุปยอดขาย, การจัดการการยืนยันตัวตน/เซสชัน, งานแบตช์, และอื่น ๆ.
หากคุณปฏิบัติตามหลักการในบทความนี้, คุณจะหลีกเลี่ยงปัญหาทั่วไปหลายอย่าง เช่น การลอยของเวลา, ข้อบกพร่องของขอบเขต, และการลดประสิทธิภาพ.


