MySQL Timestamp ปัจจุบัน: NOW(), CURRENT_TIMESTAMP, SYSDATE(), UTC, และแนวปฏิบัติที่ดีที่สุด

目次

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 caseRecommended function
General current datetimeNOW()
Table default valueCURRENT_TIMESTAMP
Date onlyCURDATE()
Time onlyCURTIME()
Unified UTC managementUTC_TIMESTAMP()
High-precision logsNOW(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 caseRecommended
Simple retrievalNOW()
Table DEFAULT / ON UPDATECURRENT_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

ความแตกต่างที่สำคัญ

FunctionWhen 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

ตัวระบุรูปแบบทั่วไป

SpecifierMeaningExample
%Y4-digit year2025
%m2-digit month02
%d2-digit day23
%HHour (24-hour)14
%iMinutes35
%sSeconds50
%fMicroseconds123456

หมายเหตุ

  • บางตัวระบุแตกต่างกันระหว่างตัวพิมพ์ใหญ่/เล็ก
  • %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

การใช้งานที่แนะนำ

GoalRecommended
Formatting for displayDATE_FORMAT
Calculation / comparisonDATE() / 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

UnitMeaning
SECONDSeconds
MINUTEMinutes
HOURHours
DAYDays
WEEKWeeks
MONTHMonths
YEARYears

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

ExpressionMeaning
NOW() - INTERVAL 1 DAYPast 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 &gt;= '2025-02-01 00:00:00'
  AND order_date &lt;= '2025-02-10 00:00:00'

ดังนั้น มันไม่รวมข้อมูลหลัง 00:00 ของวันที่ 10 ก.พ.


5.2 Safer pattern (recommended)

Recommended pattern

SELECT *
FROM orders
WHERE order_date &gt;= '2025-02-01 00:00:00'
  AND order_date &lt;  '2025-02-11 00:00:00';

Key points

  • ระบุขอบเขตสิ้นสุดเป็น “น้อยกว่าวันถัดไปเวลา 00:00”
  • ปลอดภัยกว่าการใช้ <= 23:59:59 (รองรับไมโครวินาที)

5.3 วิธีที่ถูกต้องในการดึงข้อมูลของวันนี้

ตัวอย่างที่ไม่ดี:

WHERE DATE(created_at) = CURDATE();

ปัญหา:

  • การใช้ฟังก์ชันกับคอลัมน์ ทำให้ดัชนีไม่ทำงาน
  • อาจทำให้การทำงานช้าลงอย่างมากในตารางขนาดใหญ่

แนะนำ:

WHERE created_at &gt;= CURDATE()
  AND created_at &lt;  CURDATE() + INTERVAL 1 DAY;

นี่ทำให้แน่ใจว่า:

  • สามารถใช้ดัชนีได้
  • การค้นหาเร็ว
  • ไม่มีปัญหาเรื่องขอบเขต

5.4 รูปแบบที่ปลอดภัยสำหรับ 7 วันที่ผ่านมา / 30 วันที่ผ่านมา

7 วันที่ผ่านมา

WHERE created_at &gt;= NOW() - INTERVAL 7 DAY;

30 วันที่ผ่านมา

WHERE created_at &gt;= 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 &gt;= '2025-02-23 00:00:00'
  AND created_at &lt;  '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 (สำคัญ)

TypeRangeTime zone impact
DATETIMEYear 1000 to 9999No
TIMESTAMP1970 to 2038Yes

ความแตกต่างที่สำคัญ

  • TIMESTAMP ถูกเก็บภายในเป็น UTC และแปลงเป็นโซนเวลาของเซสชันเมื่อแสดงผล
  • DATETIME เก็บค่าตามที่ระบุ (ไม่มีการแปลง).

แนวทาง

CaseRecommended type
Log managementTIMESTAMP
Future dates (after 2038)DATETIME
Fixed values not requiring TZ conversionDATETIME

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, แปลงเป็นเวลาท้องถิ่นเพื่อแสดง”

MySQL timezone architecture storing timestamps in UTC and converting to local time (JST)

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 (หลักการในโลกจริง)

นโยบายที่แนะนำ

  1. เก็บข้อมูลเป็น UTC
  2. แปลงเป็นเขตเวลาของผู้ใช้เมื่อแสดงผล

เหตุผล:

  • รองรับระดับสากลได้ง่ายขึ้น
  • หลีกเลี่ยงปัญหาเวลาออมแสง (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 &gt;= @base_time - INTERVAL 1 DAY;

เหตุผล

  • เวลาไม่เลื่อนกลางกระบวนการ
  • ความสอดคล้องถูกคงไว้

8.4 การรวมข้อมูลสำหรับวันนี้ / เมื่อวาน / 7 วันก่อน

ยอดขายวันนี้

SELECT SUM(amount)
FROM orders
WHERE created_at &gt;= CURDATE()
  AND created_at &lt;  CURDATE() + INTERVAL 1 DAY;

ยอดขายเมื่อวาน

SELECT SUM(amount)
FROM orders
WHERE created_at &gt;= CURDATE() - INTERVAL 1 DAY
  AND created_at &lt;  CURDATE();

7 วันก่อน

SELECT COUNT(*)
FROM users
WHERE created_at &gt;= NOW() - INTERVAL 7 DAY;

สำคัญ

  • อย่าใช้ฟังก์ชันกับคอลัมน์ใน WHERE clauses
  • เขียนเงื่อนไขที่รักษาให้ดัชนีใช้งานได้

8.5 การตรวจสอบการหมดอายุ (เซสชัน/โทเค็น)

SELECT *
FROM sessions
WHERE expires_at &gt; NOW();

ลบแถวที่หมดอายุ

DELETE FROM sessions
WHERE expires_at &lt;= NOW();

ข้อผิดพลาดทั่วไป

  • ใช้ CURDATE() และละเลยเวลา
  • เก็บ UTC แต่เปรียบเทียบกับ NOW() ท้องถิ่น

8.6 รับแถวที่หมดอายุภายใน N ชั่วโมง

SELECT *
FROM coupons
WHERE expires_at &lt;= 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 UPDATE
  • NOW() ไม่สามารถใช้โดยตรงใน DEFAULT

หมายเหตุ

  • ไม่ใช่ความแตกต่างของประเภท
  • คุณสามารถระบุความแม่นยำด้วยอาร์กิวเมนต์เช่น (6)

9.2 ควรใช้ SYSDATE() หรือไม่?

SYSDATE() ส่งคืนเวลาที่ “เวลาประเมิน”

SELECT SYSDATE(), SLEEP(2), SYSDATE();

ค่าอาจเปลี่ยนแปลงแม้ใน query เดียวกัน

เมื่อใดควรใช้

  • เมื่อคุณต้องการบันทึกเวลาจริงที่แน่นอน

เมื่อใดควรหลีกเลี่ยง

  • การทำซ้ำ (Replication)
  • การประมวลผลธุรกรรมที่ความสอดคล้องสำคัญ

ในกรณีส่วนใหญ่ การใช้ NOW() ก็เพียงพอ

9.3 ทำไมเวลาถึงเลื่อน?

สาเหตุหลัก:

  1. การตั้งค่าโซนเวลาเซิร์ฟเวอร์
  2. การตั้งค่าโซนเวลาเซสชัน
  3. การแปลงซ้ำสองครั้งกับแอปพลิเคชัน
  4. พฤติกรรมการแปลงอัตโนมัติของประเภท 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 &gt;= '2025-02-01 00:00:00'
  AND created_at &lt;  '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 &gt;= 'START'
  AND created_at &lt;  '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

  1. อย่าเข้าใจผิดความแตกต่างระหว่าง NOW() และ CURRENT_TIMESTAMP
  2. สำหรับการค้นหาช่วง, ใช้ >= AND <
  3. เก็บเป็น UTC โดยค่าเริ่มต้น
  4. ตัดสินใจประเภท (DATETIME vs TIMESTAMP) ในขั้นตอนการออกแบบ

การจัดการเวลาปัจจุบันของ MySQL เป็นพื้นฐานสำหรับการบันทึก, การสรุปยอดขาย, การจัดการการยืนยันตัวตน/เซสชัน, งานแบตช์, และอื่น ๆ.
หากคุณปฏิบัติตามหลักการในบทความนี้, คุณจะหลีกเลี่ยงปัญหาทั่วไปหลายอย่าง เช่น การลอยของเวลา, ข้อบกพร่องของขอบเขต, และการลดประสิทธิภาพ.