- 1 1. Câu lệnh SQL ngắn nhất để lấy ngày/giờ hiện tại trong MySQL
- 1.1 1.1 Lấy ngày/giờ hiện tại (cơ bản)
- 1.2 1.2 Lấy chỉ ngày hiện tại
- 1.3 1.3 Lấy chỉ thời gian hiện tại
- 1.4 1.4 Lấy thời gian hiện tại ở UTC (quan trọng)
- 1.5 1.5 Lấy mili giây / micro giây
- 1.6 1.6 Hàm tốt nhất theo trường hợp sử dụng (bắt đầu ở đây nếu không chắc)
- 1.7 Các điểm quan trọng mà người dùng thường bỏ qua
- 2 2. Sự khác nhau giữa NOW() / CURRENT_TIMESTAMP / SYSDATE()
- 3 3. Thay đổi định dạng hiển thị của ngày/giờ hiện tại
- 4 4. Các phép tính datetime sử dụng thời gian hiện tại
- 4.1 4.1 Thêm/bớt với INTERVAL
- 4.2 4.2 Lấy dữ liệu trong 24 giờ qua (mẫu phổ biến nhất)
- 4.3 4.3 Lấy sự chênh lệch ngày bằng DATEDIFF()
- 4.4 4.4 Sử dụng TIMESTAMPDIFF() cho giờ/phút/giây
- 4.5 4.5 Lấy ngày đầu tháng / ngày cuối tháng (cạm bẫy thực tế phổ biến)
- 4.6 Tóm tắt các lỗi thường gặp
- 4.7 Những điểm chính cần nhớ từ phần này
- 5 5. Đối với truy vấn phạm vi, cách này an toàn hơn BETWEEN
- 6 6. DEFAULT CURRENT_TIMESTAMP và ON UPDATE (cơ bản thiết kế bảng)
- 6.1 6.1 Tự động đặt created_at (DEFAULT CURRENT_TIMESTAMP)
- 6.2 6.2 Tự động cập nhật updated_at (ON UPDATE)
- 6.3 6.3 DATETIME vs TIMESTAMP (quan trọng)
- 6.4 6.4 CURRENT_TIMESTAMP cũng có thể dùng với DATETIME
- 6.5 6.5 Nếu bạn muốn dùng NOW(): một cách thay thế (trigger)
- 6.6 Các lỗi thiết kế phổ biến
- 6.7 Những điểm chính từ phần này
- 7 7. Thiết kế múi giờ (lưu trữ ở UTC, hiển thị theo thời gian địa phương)
- 8 8. Các ví dụ thực tế có thể sử dụng ngay
- 8.1 8.1 Tự động chèn thời gian hiện tại vào logs
- 8.2 8.2 Cập nhật thời gian đăng nhập cuối cùng
- 8.3 8.3 Đặt thời gian tham chiếu cố định trong các công việc batch
- 8.4 8.4 Aggregation for today / yesterday / past 7 days
- 8.5 8.5 Expiration checks (sessions/tokens)
- 8.6 8.6 Get rows expiring within N hours
- 8.7 Những điều bạn luôn phải nhớ
- 8.8 Những điểm chính rút ra từ phần này
- 9 9. Frequently Asked Questions (FAQ)
- 9.1 9.1 Sự khác nhau giữa NOW() và CURRENT_TIMESTAMP là gì?
- 9.2 9.2 Bạn có nên dùng SYSDATE() không?
- 9.3 9.3 Tại sao thời gian lại bị dịch?
- 9.4 9.4 CONVERT_TZ() returns NULL
- 9.5 9.5 Range shifts with BETWEEN
- 9.6 9.6 How do you choose between DATETIME and TIMESTAMP?
- 9.7 9.7 Microseconds aren’t being stored
- 9.8 Những điểm chính cần nhớ từ phần này
- 10 10. Summary
1. Câu lệnh SQL ngắn nhất để lấy ngày/giờ hiện tại trong MySQL
Nếu bạn muốn lấy ngày/giờ hiện tại trong MySQL, hai hàm đầu tiên cần nhớ là NOW() và CURRENT_TIMESTAMP. Dưới đây là các ví dụ SQL ngắn nhất theo từng trường hợp sử dụng.
1.1 Lấy ngày/giờ hiện tại (cơ bản)
SELECT NOW();
hoặc
SELECT CURRENT_TIMESTAMP;
- Cả hai đều trả về ngày/giờ hiện tại (YYYY-MM-DD HH:MM:SS).
- Trong cùng một truy vấn, thời gian được cố định tại “thời điểm bắt đầu truy vấn”.
Các trường hợp sử dụng
- Ghi log
- Lấy dấu thời gian tạo bản ghi
- Lấy thời gian tham chiếu
Nhầm lẫn thường gặp
- Múi giờ của ứng dụng và DB khác nhau gây “dịch chuyển thời gian”. → Kiểm tra bằng
SELECT @@session.time_zone;
1.2 Lấy chỉ ngày hiện tại
SELECT CURDATE();
- Định dạng trả về:
YYYY-MM-DD - Kiểu dữ liệu là DATE (chỉ ngày, không có thời gian)
Các trường hợp sử dụng
- Tìm kiếm “dữ liệu hôm nay”
- Tổng hợp hàng ngày
Lưu ý
- Không giống như
NOW(), nó không bao gồm thời gian. - Nếu bạn cần thời gian cho các truy vấn phạm vi, hãy dùng
NOW().
1.3 Lấy chỉ thời gian hiện tại
SELECT CURTIME();
- Định dạng trả về:
HH:MM:SS - Kiểu dữ liệu là TIME (chỉ thời gian)
Các trường hợp sử dụng
- Hiển thị thời gian thực thi batch
- In ra chỉ phần thời gian trong log
Cạm bẫy thường gặp
- Vì không có ngày, bạn không thể dùng nó cho các so sánh datetime.
1.4 Lấy thời gian hiện tại ở UTC (quan trọng)
SELECT UTC_TIMESTAMP();
- Trả về UTC bất kể múi giờ máy chủ.
- Được khuyến nghị cho các dịch vụ toàn cầu.
Chính sách cơ bản trong dự án thực tế
- Lưu trữ ở UTC
- Chuyển đổi sang giờ địa phương khi hiển thị
Lưu ý
- Nếu ứng dụng đã tự chuyển sang UTC, hãy cẩn thận tránh chuyển đổi gấp đôi.
1.5 Lấy mili giây / micro giây
SELECT NOW(3); -- milliseconds (3 digits after the decimal point)
SELECT NOW(6); -- microseconds (6 digits after the decimal point)
- Có sẵn từ MySQL 5.6.4 trở lên.
- Dùng cho log có độ chính xác cao và kiểm toán giao dịch.
Nhận định sai lầm phổ biến
NOW()một mình không thể trả về phần thập phân giây.- Cột của bạn cũng phải chỉ định độ chính xác, ví dụ
DATETIME(6).
1.6 Hàm tốt nhất theo trường hợp sử dụng (bắt đầu ở đây nếu không chắc)
| 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) |
Các điểm quan trọng mà người dùng thường bỏ qua
NOW()vàCURRENT_TIMESTAMPthực chất tương đương (cùng giá trị)- Trong cùng một truy vấn, thời gian được cố định
- Giá trị hiển thị thay đổi tùy theo cài đặt múi giờ
- Đối với microseconds, định nghĩa cột của bạn phải chỉ định độ chính xác
2. Sự khác nhau giữa NOW() / CURRENT_TIMESTAMP / SYSDATE()
Có nhiều hàm để lấy thời gian hiện tại, nhưng phần gây nhầm lẫn nhất là sự khác biệt giữa NOW(), CURRENT_TIMESTAMP và SYSDATE().
Nếu bạn không hiểu đúng, có thể gặp hành vi bất ngờ trong log và xử lý giao dịch.
2.1 NOW() và CURRENT_TIMESTAMP thực chất tương đương
SELECT NOW(), CURRENT_TIMESTAMP;
- Cả hai đều trả về datetime hiện tại (giá trị DATETIME).
- Trong cùng một truy vấn, thời gian được cố định tại “thời điểm bắt đầu truy vấn”.
CURRENT_TIMESTAMPcũng có thể được dùng với cú pháp gọi hàm:SELECT CURRENT_TIMESTAMP();
Cách chọn trong dự án thực tế
| Use case | Recommended |
|---|---|
| Simple retrieval | NOW() |
| Table DEFAULT / ON UPDATE | CURRENT_TIMESTAMP |
Các điểm quan trọng
- Ý nghĩa của giá trị lấy được là giống nhau.
- Sự khác biệt chính là cú pháp / cách sử dụng (ví dụ, cho phép trong DEFAULT).
- Không phải là sự khác biệt về kiểu dữ liệu (dễ gây hiểu lầm).
2.2 SYSDATE() trả về thời gian tại “thời điểm đánh giá”
SYSDATE() trông giống NOW(), nhưng thời điểm giá trị được cố định là khác nhau.
SELECT NOW(), SLEEP(2), NOW();
Kết quả (ví dụ):
2025-02-23 14:00:00
2025-02-23 14:00:00
SELECT SYSDATE(), SLEEP(2), SYSDATE();
Kết quả (ví dụ):
2025-02-23 14:00:00
2025-02-23 14:00:02
Sự khác biệt cốt lõi
| Function | When the time is fixed |
|---|---|
NOW() | Query start time |
SYSDATE() | Evaluation time |
2.3 Ghi chú cho giao dịch và sao chép
Vì NOW() được cố định tại thời điểm bắt đầu truy vấn, nó an toàn hơn khi bạn cần một thời gian tham chiếu nhất quán trong một giao dịch.
Ngược lại, vì SYSDATE() phụ thuộc vào thời gian thực thi, nó có thể ảnh hưởng đến khả năng tái tạo trong:
- Sao chép
- Công việc batch
- Xử lý log hàng loạt
Nguyên tắc chung
- Muốn thời gian tham chiếu cố định →
NOW() - Cần thời điểm chính xác mỗi lần →
SYSDATE()(sử dụng hạn chế)
2.4 Ghi chú: CURRENT_DATE / CURRENT_TIME / LOCALTIME
MySQL cũng hỗ trợ:
SELECT CURRENT_DATE;
SELECT CURRENT_TIME;
SELECT LOCALTIME;
CURRENT_DATE→ tương tựCURDATE()CURRENT_TIME→ tương tựCURTIME()LOCALTIME→ tương tựNOW()
Nhầm lẫn thường gặp
- Chúng chủ yếu là “khác biệt về hình thức”; thực chất có ít khác biệt chức năng.
- Để an toàn, ưu tiên tính dễ đọc và chuẩn hoá trong dự án.
Những điểm chính cần nhớ từ phần này
NOW()vàCURRENT_TIMESTAMPvề cơ bản có cùng ý nghĩa.SYSDATE()hoạt động khác vì nó dựa trên thời gian đánh giá.- Đối với thời gian tham chiếu cố định, sử dụng
NOW()làm mặc định. - Sử dụng
CURRENT_TIMESTAMPcho DEFAULT và ON UPDATE.
3. Thay đổi định dạng hiển thị của ngày/giờ hiện tại
Thời gian hiện tại bạn nhận được bằng NOW() mặc định được hiển thị dưới dạng YYYY-MM-DD HH:MM:SS. Trong công việc thực tế, bạn thường cần các dạng như:
- Chỉ hiển thị ngày
- Sử dụng
YYYY/MM/DD - Sử dụng định dạng địa phương (ví dụ: 23 Tháng 2, 2025)
- Hiển thị mili giây
Để làm điều này, sử dụng DATE_FORMAT() (chuyển đổi datetime sang một chuỗi định dạng tùy ý).
3.1 Cú pháp cơ bản của DATE_FORMAT()
DATE_FORMAT(datetime, 'format_string')
Ví dụ: Chuyển đổi thời gian hiện tại sang YYYY/MM/DD HH:MM
SELECT DATE_FORMAT(NOW(), '%Y/%m/%d %H:%i');
Ví dụ đầu ra:
2025/02/23 14:35
Các ký hiệu định dạng thường dùng
| 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 |
Ghi chú
- Một số ký hiệu khác nhau giữa chữ hoa và chữ thường.
%hlà giờ 12 giờ,%Hlà giờ 24 giờ.
3.2 Các ví dụ định dạng thường gặp (thường trong công việc thực tế)
1. Ngăn cách bằng dấu gạch chéo
SELECT DATE_FORMAT(NOW(), '%Y/%m/%d');
2. Định dạng địa phương (Tiếng Anh)
SELECT DATE_FORMAT(NOW(), '%b %d, %Y');
3. Không có giây
SELECT DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i');
Cạm bẫy thường gặp
- Vì nó trả về một chuỗi, bạn không thể dùng nó cho các phép tính số.
- Đối với so sánh và tìm kiếm, bạn nên dùng kiểu DATETIME gốc.
3.3 Trích xuất chỉ phần ngày hoặc thời gian
Bạn cũng có thể lấy các phần riêng trong khi giữ nguyên kiểu dữ liệu (thay vì định dạng thành chuỗi).
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
Cách sử dụng đề xuất
| Goal | Recommended |
|---|---|
| Formatting for display | DATE_FORMAT |
| Calculation / comparison | DATE() / TIME() |
3.4 Hiển thị với microseconds
Khi bạn cần log có độ chính xác cao:
SELECT NOW(6);
Với định dạng:
SELECT DATE_FORMAT(NOW(6), '%Y-%m-%d %H:%i:%s.%f');
Lưu ý quan trọng
- Cột của bạn phải là
DATETIME(6)hoặcTIMESTAMP(6), nếu không độ chính xác sẽ không được lưu. - Không khả dụng trong các phiên bản MySQL cũ hơn 5.6.4 (phụ thuộc vào môi trường).
Những lỗi thường gặp
Sử dụng
DATE_FORMAT()trong mệnh đề WHERE sẽ ngăn các chỉ mục được sử dụng. wp:list /wp:list- Bad example:
WHERE DATE_FORMAT(created_at, '%Y-%m-%d') = '2025-02-23' - Recommended:
WHERE created_at >= '2025-02-23 00:00:00' AND created_at < '2025-02-24 00:00:00' - Lưu trực tiếp chuỗi đã định dạng hiển thị (gây giảm hiệu suất tìm kiếm)
- Bad example:
Những điểm chính cần nhớ từ phần này
- Sử dụng
DATE_FORMAT()để thay đổi định dạng hiển thị. - Đối với các phép tính/so sánh, giữ nguyên kiểu dữ liệu gốc.
- Nếu cần microseconds, chỉ định độ chính xác trong định nghĩa cột.
- Sử dụng hàm trong mệnh đề WHERE có thể gây vấn đề hiệu năng.
4. Các phép tính datetime sử dụng thời gian hiện tại
In MySQL, thường xuyên tính toán các thứ như “N giờ sau,” “N ngày trước,” hoặc “N ngày qua” dựa trên thời gian hiện tại.
Trường hợp thực tế phổ biến nhất là “lấy dữ liệu trong 24 giờ qua.”
Nền tảng của phép tính ngày giờ là INTERVAL (cú pháp để cộng/trừ các đơn vị thời gian).
4.1 Thêm/bớt với INTERVAL
Lấy 1 giờ sau
SELECT NOW() + INTERVAL 1 HOUR;
Lấy 3 ngày trước
SELECT NOW() - INTERVAL 3 DAY;
Lấy 1 tháng sau
SELECT NOW() + INTERVAL 1 MONTH;
Các đơn vị thường dùng
| Unit | Meaning |
|---|---|
| SECOND | Seconds |
| MINUTE | Minutes |
| HOUR | Hours |
| DAY | Days |
| WEEK | Weeks |
| MONTH | Months |
| YEAR | Years |
Những lỗi thường gặp
DAYtrongINTERVAL 1 DAYvẫn hoạt động ngay cả khi viết thường, nhưng bạn nên chuẩn hoá trong nhóm.- Các phép tính cuối tháng có thể khác với mong đợi vì số ngày thay đổi (ví dụ, 31/1 + 1 tháng).
4.2 Lấy dữ liệu trong 24 giờ qua (mẫu phổ biến nhất)
SELECT *
FROM users
WHERE created_at >= NOW() - INTERVAL 1 DAY;
Ý nghĩa
- Nhắm tới “từ ngay bây giờ lùi lại 24 giờ trước.”
Lưu ý
- Nếu bạn dùng
CURDATE(), cơ sở sẽ trở thành “hôm nay lúc 00:00”, điều này thay đổi ý nghĩa.
Ví dụ (dữ liệu của hôm nay):
SELECT *
FROM users
WHERE created_at >= CURDATE();
Hiểu sự khác biệt là rất quan trọng
| Expression | Meaning |
|---|---|
NOW() - INTERVAL 1 DAY | Past 24 hours |
CURDATE() | Since today 00:00 |
4.3 Lấy sự chênh lệch ngày bằng DATEDIFF()
SELECT DATEDIFF('2025-03-01', '2025-02-23');
Kết quả:
6
- Đơn vị là “ngày”
- Phần thời gian bị bỏ qua
Lưu ý
- Dấu sẽ thay đổi tùy theo thứ tự đối số.
- Bạn không thể tính sự chênh lệch theo giờ/phút/giây.
4.4 Sử dụng TIMESTAMPDIFF() cho giờ/phút/giây
SELECT TIMESTAMPDIFF(HOUR,
'2025-02-23 12:00:00',
'2025-02-23 18:30:00');
Kết quả:
6
Phút
SELECT TIMESTAMPDIFF(MINUTE,
'2025-02-23 12:00:00',
'2025-02-23 12:30:00');
Giây
SELECT TIMESTAMPDIFF(SECOND,
'2025-02-23 12:00:00',
'2025-02-23 12:00:45');
Các trường hợp sử dụng
- Tính thời lượng phiên làm việc
- Kiểm tra thời hạn
- Quyết định timeout
4.5 Lấy ngày đầu tháng / ngày cuối tháng (cạm bẫy thực tế phổ biến)
Lấy ngày đầu tiên của tháng này:
SELECT DATE_FORMAT(NOW(), '%Y-%m-01');
Ngày đầu tiên của tháng tới:
SELECT DATE_FORMAT(NOW() + INTERVAL 1 MONTH, '%Y-%m-01');
Lưu ý
- Ngày cuối tháng không phải là một ngày cố định.
- Đối với các truy vấn phạm vi, “>= start AND < first day of next month” là an toàn.
Tóm tắt các lỗi thường gặp
- Sử dụng
BETWEENvới giá trị chỉ ngày và quên thời gian - Vô hiệu hoá chỉ mục bằng cách dùng
DATE(created_at)trong mệnh đề WHERE - Bị mắc kẹt vào vấn đề “ngày 31” trong tính toán tháng
- Hiểu sai sự khác biệt giữa
NOW()vàCURDATE()
Những điểm chính cần nhớ từ phần này
INTERVALlà nền tảng của phép tính ngày giờ.- 24 giờ qua =
NOW() - INTERVAL 1 DAY. - Ngày =
DATEDIFF(); các đơn vị nhỏ hơn =TIMESTAMPDIFF(). - Cẩn thận với các điều kiện kết thúc trong tính toán dựa trên tháng.
5. Đối với truy vấn phạm vi, cách này an toàn hơn BETWEEN
Khi thực hiện tìm kiếm phạm vi ngày trong MySQL, nhiều người mới bắt đầu dùng BETWEEN. Tuy nhiên, nó thường tạo ra kết quả không mong muốn nếu bạn xử lý sai ranh giới thời gian, vì vậy một mẫu an toàn hơn được khuyến nghị trong công việc thực tế.
5.1 Cơ bản về BETWEEN và cạm bẫy
Một truy vấn phổ biến
SELECT *
FROM orders
WHERE order_date BETWEEN '2025-02-01' AND '2025-02-10';
Nó trông ổn, nhưng bên trong nó tương đương với:
WHERE order_date >= '2025-02-01 00:00:00'
AND order_date <= '2025-02-10 00:00:00'
Vì vậy nó không bao gồm dữ liệu sau 00:00 ngày 10/2.
5.2 Mẫu an toàn hơn (được khuyến nghị)
Mẫu được khuyến nghị
SELECT *
FROM orders
WHERE order_date >= '2025-02-01 00:00:00'
AND order_date < '2025-02-11 00:00:00';
Các điểm chính
- Xác định ranh giới cuối là “nhỏ hơn ngày tiếp theo vào 00:00”
- An toàn hơn
<= 23:59:59(hỗ trợ microseconds)
5.3 Cách đúng để lấy dữ liệu của hôm nay
Ví dụ sai:
WHERE DATE(created_at) = CURDATE();
Vấn đề:
- Áp dụng một hàm lên cột vô hiệu hoá chỉ mục
- Có thể gây chậm đáng kể trên các bảng lớn
Đề xuất:
WHERE created_at >= CURDATE()
AND created_at < CURDATE() + INTERVAL 1 DAY;
Điều này đảm bảo:
- Có thể sử dụng chỉ mục
- Tìm kiếm nhanh
- Không có vấn đề về ranh giới
5.4 Các mẫu an toàn cho 7 ngày qua / 30 ngày qua
7 ngày qua
WHERE created_at >= NOW() - INTERVAL 7 DAY;
30 ngày qua
WHERE created_at >= NOW() - INTERVAL 30 DAY;
Ghi chú
CURDATE() - INTERVAL 7 DAYdựa trên “hôm nay 00:00”NOW() - INTERVAL 7 DAYdựa trên “thời gian hiện tại”- Chọn dựa trên yêu cầu
5.5 Quy tắc chính để giữ chỉ mục hiệu quả
Sai:
WHERE DATE(created_at) = '2025-02-23';
Đúng:
WHERE created_at >= '2025-02-23 00:00:00'
AND created_at < '2025-02-24 00:00:00';
Tại sao:
- Chỉ mục hoạt động trên “cột tự nó”
- Áp dụng hàm ngăn việc sử dụng chỉ mục (quét toàn bộ)
Common mistakes summary
- Quên bao gồm thời gian ở ranh giới cuối trong
BETWEEN - Thiếu microseconds khi dùng
23:59:59 - Sử dụng
DATE()trong mệnh đề WHERE và làm chậm truy vấn - Để “now” và “today 00:00” không rõ ràng
Key takeaways from this section
- Truy vấn phạm vi an toàn nhất với
>= start AND < end. BETWEENyêu cầu xử lý ranh giới cẩn thận.- Để giữ chỉ mục hiệu quả, không nên bao bọc cột trong hàm.
- Rõ ràng chọn giữa logic dựa trên
NOW()vàCURDATE().
6. DEFAULT CURRENT_TIMESTAMP và ON UPDATE (cơ bản thiết kế bảng)
Trong thiết kế cơ sở dữ liệu, thường tự động quản lý created_at và updated_at.
Trong MySQL, CURRENT_TIMESTAMP cho phép bạn đặt thời gian hiện tại tự động khi chèn và cập nhật.
6.1 Tự động đặt created_at (DEFAULT CURRENT_TIMESTAMP)
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Hành vi:
INSERT INTO users (name) VALUES ('Alice');
→ Thời gian hiện tại được tự động chèn vào created_at.
Các điểm chính
CURRENT_TIMESTAMPcó thể được dùng trong DEFAULT.NOW()không thể được dùng trực tiếp trong DEFAULT.
Lỗi thường gặp
created_at DATETIME DEFAULT NOW(); -- error
Lý do:
- Trong MySQL, bạn thường không thể đặt một hàm làm DEFAULT (ngoại lệ duy nhất là CURRENT_TIMESTAMP).
6.2 Tự động cập nhật 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
);
Hành vi:
UPDATE users SET name = 'Bob' WHERE id = 1;
→ updated_at được tự động cập nhật thành thời gian hiện tại.
Các trường hợp sử dụng
- Theo dõi lần đăng nhập cuối cùng
- Lịch sử cập nhật
- Nhật ký kiểm toán
6.3 DATETIME vs TIMESTAMP (quan trọng)
| Type | Range | Time zone impact |
|---|---|---|
| DATETIME | Year 1000 to 9999 | No |
| TIMESTAMP | 1970 to 2038 | Yes |
Sự khác biệt cốt lõi
TIMESTAMPđược lưu nội bộ ở UTC và chuyển đổi sang múi giờ phiên làm việc khi hiển thị.DATETIMElưu giá trị nguyên (không chuyển đổi).
Hướng dẫn
| Case | Recommended type |
|---|---|
| Log management | TIMESTAMP |
| Future dates (after 2038) | DATETIME |
| Fixed values not requiring TZ conversion | DATETIME |
6.4 CURRENT_TIMESTAMP cũng có thể dùng với DATETIME
Trong MySQL 5.6 trở lên, bạn có thể chỉ định CURRENT_TIMESTAMP làm DEFAULT và ON UPDATE cho các cột DATETIME cũng được.
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
Ghi chú
- Các phiên bản MySQL cũ hơn có hạn chế (phụ thuộc vào môi trường).
- Nếu bạn cần độ chính xác:
DATETIME(6) DEFAULT CURRENT_TIMESTAMP(6)
6.5 Nếu bạn muốn dùng NOW(): một cách thay thế (trigger)
CREATE TRIGGER set_created_at
BEFORE INSERT ON logs
FOR EACH ROW
SET NEW.created_at = NOW();
Các trường hợp sử dụng
- Logic giá trị ban đầu phức tạp
- Gán dấu thời gian có điều kiện
Ghi chú
- Triggers khó debug.
- Tránh sử dụng chúng trừ khi cần thiết.
Các lỗi thiết kế phổ biến
- Thêm ON UPDATE vào cả
created_atvàupdated_at - Nhầm lẫn hành vi của DATETIME và TIMESTAMP
- Hoãn quyết định múi giờ
Những điểm chính từ phần này
- Sử dụng
CURRENT_TIMESTAMPcho DEFAULT. - Sử dụng
ON UPDATE CURRENT_TIMESTAMPđể theo dõi cập nhật. - Chọn loại dựa trên hành vi múi giờ và vấn đề năm 2038.
- Nếu cần độ chính xác, đừng quên
(6).
7. Thiết kế múi giờ (lưu trữ ở UTC, hiển thị theo thời gian địa phương)
Khi xử lý thời gian hiện tại, thiết kế múi giờ kém gây ra lệch thời gian và chuyển đổi kép.
Trong thực tế, mặc định an toàn nhất là “lưu trữ ở UTC, chuyển đổi sang thời gian địa phương để hiển thị.”

Hình: Kiến trúc cơ bản để lưu trữ UTC trong MySQL và hiển thị thời gian địa phương
7.1 Kiểm tra múi giờ hiện tại
Đầu tiên, kiểm tra múi giờ mà MySQL đang chạy.
SELECT @@global.time_zone, @@session.time_zone;
@@global.time_zone→ cài đặt toàn server@@session.time_zone→ cài đặt kết nối/phiên hiện tại- Nếu hiển thị
SYSTEM, nó phụ thuộc vào cài đặt OS
Các vấn đề phổ biến
- Server sản xuất và phát triển có múi giờ OS khác nhau
- Ứng dụng chuyển đổi sang UTC, và DB chuyển đổi lại (chuyển đổi kép)
7.2 Thay đổi múi giờ theo phiên
SET time_zone = 'Asia/Tokyo';
hoặc thống nhất sang UTC:
SET time_zone = '+00:00';
Các điểm quan trọng
- Nó sẽ trở về mặc định khi kết nối đóng
- Nếu sử dụng connection pooling, kiểm tra cài đặt ứng dụng của bạn
7.3 Tại sao lưu trữ ở UTC (nguyên tắc thực tế)
Chính sách khuyến nghị
- Lưu trữ dữ liệu ở UTC
- Chuyển đổi sang múi giờ của người dùng khi hiển thị
Lý do:
- Hỗ trợ quốc tế dễ dàng hơn
- Tránh vấn đề giờ tiết kiệm ánh sáng ban ngày (DST)
- Giảm thiểu tác động khi di chuyển server
Lấy UTC:
SELECT UTC_TIMESTAMP();
7.4 Chuyển đổi múi giờ với CONVERT_TZ()
Ví dụ: UTC → JST
SELECT CONVERT_TZ('2025-02-23 05:35:00', '+00:00', '+09:00');
Sử dụng tên múi giờ:
SELECT CONVERT_TZ('2025-02-23 05:35:00', 'UTC', 'Asia/Tokyo');
Ví dụ thực tế
SELECT CONVERT_TZ(created_at, 'UTC', 'Asia/Tokyo')
FROM users;
7.5 Tại sao CONVERT_TZ() trả về NULL
Nếu trả về NULL, các nguyên nhân phổ biến bao gồm:
- Bảng múi giờ của MySQL chưa được tải
- Tên múi giờ được chỉ định không tồn tại
Ví dụ tải trên Linux:
mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql
Ghi chú
- Trong sản xuất, xác nhận quyền hạn và xem có cần khởi động lại không
- Trong Docker, zoneinfo có thể không được bao gồm tùy thuộc vào image
7.6 Thay đổi múi giờ toàn server
Tệp cấu hình (my.cnf / my.ini):
[mysqld]
default_time_zone = '+00:00'
Xác nhận sau khi khởi động lại:
SELECT @@global.time_zone;
Quan trọng
- Đối với thay đổi sản xuất, xác nhận phạm vi tác động
- Cẩn thận với dữ liệu hiện có (đôi khi chỉ thay đổi hiển thị)
Các lỗi phổ biến
- Lưu trữ thời gian địa phương thay vì UTC, sau đó thêm hỗ trợ quốc tế
- Chuyển đổi kép giữa ứng dụng và DB
- Thiết kế với DATETIME mà không xem xét múi giờ
- Cài đặt TZ khác nhau giữa kiểm thử và sản xuất
Những điểm chính từ phần này
- Lưu trữ ở UTC và chuyển đổi khi hiển thị.
- Sử dụng
UTC_TIMESTAMP(). - Khi sử dụng
CONVERT_TZ(), xác nhận bảng TZ. - Luôn tính đến sự khác biệt môi trường về múi giờ.
8. Các ví dụ thực tế có thể sử dụng ngay
Dưới đây là các ví dụ SQL cụ thể cho thấy cách sử dụng thời gian hiện tại của MySQL trong phát triển/vận hành thực tế.
Chúng được viết để bạn có thể sao chép và dán trực tiếp.
8.1 Tự động chèn thời gian hiện tại vào logs
Tạo bảng
CREATE TABLE system_logs (
id INT AUTO_INCREMENT PRIMARY KEY,
level VARCHAR(50),
message TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Chèn log
INSERT INTO system_logs (level, message)
VALUES ('ERROR', 'Failed to connect to the DB');
Các điểm chính
created_atđược chèn tự động- Không cần truyền timestamp từ ứng dụng
- Thiết kế thiết yếu cho việc kiểm toán và điều tra sự cố
Những lỗi thường gặp
- Quản lý thời gian cả trong ứng dụng và DB
- Không đồng nhất thời gian do múi giờ không thống nhất
8.2 Cập nhật thời gian đăng nhập cuối cùng
Thiết kế bảng
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255),
last_login TIMESTAMP DEFAULT CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP
);
Cập nhật khi đăng nhập
UPDATE users
SET last_login = NOW()
WHERE id = 1;
Ghi chú
ON UPDATEchỉ kích hoạt khi cột thay đổi- Một số câu lệnh UPDATE có thể không thay đổi giá trị và do đó không cập nhật
8.3 Đặt thời gian tham chiếu cố định trong các công việc batch
Đối với các quy trình chạy lâu, an toàn hơn khi đặt một thời gian tham chiếu cố định.
SET @base_time = NOW();
SELECT *
FROM orders
WHERE created_at >= @base_time - INTERVAL 1 DAY;
Tại sao
- Thời gian không thay đổi giữa quá trình
- Độ nhất quán được duy trì
8.4 Aggregation for today / yesterday / past 7 days
Doanh thu hôm nay
SELECT SUM(amount)
FROM orders
WHERE created_at >= CURDATE()
AND created_at < CURDATE() + INTERVAL 1 DAY;
Doanh thu hôm qua
SELECT SUM(amount)
FROM orders
WHERE created_at >= CURDATE() - INTERVAL 1 DAY
AND created_at < CURDATE();
7 ngày qua
SELECT COUNT(*)
FROM users
WHERE created_at >= NOW() - INTERVAL 7 DAY;
Quan trọng
- Không áp dụng hàm lên cột trong mệnh đề WHERE
- Viết điều kiện sao cho chỉ mục vẫn được sử dụng
8.5 Expiration checks (sessions/tokens)
SELECT *
FROM sessions
WHERE expires_at > NOW();
Xóa các hàng đã hết hạn
DELETE FROM sessions
WHERE expires_at <= NOW();
Những lỗi thường gặp
- Sử dụng
CURDATE()và bỏ qua thời gian - Lưu UTC nhưng so sánh với
NOW()địa phương
8.6 Get rows expiring within N hours
SELECT *
FROM coupons
WHERE expires_at <= NOW() + INTERVAL 1 HOUR;
Các trường hợp sử dụng:
- Cảnh báo hết hạn
- Thông báo hết hạn
Những điều bạn luôn phải nhớ
- Rõ ràng baseline là “now” hay “today 00:00”
- Viết các điều kiện sao cho chỉ mục vẫn hiệu quả
- Quyết định thiết kế múi giờ từ đầu
- Không trộn
NOW()vàUTC_TIMESTAMP()nếu không có chính sách rõ ràng
Những điểm chính rút ra từ phần này
- Đối với log/kiểm toán/theo dõi cập nhật, sử dụng
CURRENT_TIMESTAMP - Đối với tổng hợp, sử dụng mẫu an toàn
>= AND < - Đối với quản lý phiên, so sánh với
NOW() - Đặt thời gian baseline để giữ quá trình nhất quán
9. Frequently Asked Questions (FAQ)
Dưới đây là các câu hỏi thường gặp về thời gian hiện tại trong MySQL, đặc biệt là những bẫy thường gặp trong thực tế.
9.1 Sự khác nhau giữa NOW() và CURRENT_TIMESTAMP là gì?
Kết luận: ý nghĩa của giá trị trả về là giống nhau.
SELECT NOW(), CURRENT_TIMESTAMP;
Cả hai đều trả về ngày giờ hiện tại.
Sự khác nhau chính là cách sử dụng cú pháp
CURRENT_TIMESTAMPcó thể được dùng trongDEFAULTvàON UPDATENOW()không thể dùng trực tiếp trong DEFAULT
Ghi chú
- Không phải là sự khác biệt về kiểu dữ liệu
- Bạn có thể chỉ định độ chính xác bằng các đối số như
(6)
9.2 Bạn có nên dùng SYSDATE() không?
SYSDATE() trả về thời gian tại “thời điểm đánh giá”.
SELECT SYSDATE(), SLEEP(2), SYSDATE();
Giá trị có thể thay đổi ngay cả trong cùng một truy vấn.
Khi nào nên dùng
- Khi bạn cần ghi lại thời điểm thực tế chính xác
Khi nào nên tránh
- Sao chép
- Xử lý giao dịch nơi tính nhất quán quan trọng
Trong hầu hết các trường hợp, sử dụng NOW() là đủ.
9.3 Tại sao thời gian lại bị dịch?
Nguyên nhân chính:
- Cài đặt múi giờ máy chủ
- Cài đặt múi giờ phiên
- Chuyển đổi đôi trong ứng dụng
- Hành vi chuyển đổi tự động của kiểu TIMESTAMP
Kiểm tra bằng:
SELECT @@global.time_zone, @@session.time_zone;
Giảm thiểu
- Lưu trữ ở UTC theo mặc định
- Chỉ chuyển đổi khi hiển thị
- Đồng nhất chính sách giữa ứng dụng và CSDL
9.4 CONVERT_TZ() returns NULL
Nguyên nhân:
- Bảng múi giờ chưa được tải
- Tên múi giờ không chính xác
Cách khắc phục:
mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql
Hãy đặc biệt cẩn thận trong môi trường Docker.
9.5 Range shifts with BETWEEN
Sai:
WHERE created_at BETWEEN '2025-02-01' AND '2025-02-10';
Đúng:
WHERE created_at >= '2025-02-01 00:00:00'
AND created_at < '2025-02-11 00:00:00';
Lý do:
- Vấn đề thời gian ranh giới cuối
- Rò rỉ microgiây
- Hiệu suất chỉ mục
9.6 How do you choose between DATETIME and TIMESTAMP?
- Hỗ trợ quốc tế / Quản lý UTC → TIMESTAMP
- Sau năm 2038 hoặc ngày giờ cố định → DATETIME
Quyết định điều này trong giai đoạn thiết kế.
9.7 Microseconds aren’t being stored
Nguyên nhân:
- Không chỉ định độ chính xác trong định nghĩa cột
Cách khắc phục:
created_at DATETIME(6)
Những điểm chính cần nhớ từ phần này
- Bắt đầu với
NOW()vàCURRENT_TIMESTAMP - Đối với truy vấn phạm vi, sử dụng
>= AND < - Lưu trữ UTC là mặc định an toàn nhất
- Đừng quên lựa chọn kiểu dữ liệu và độ chính xác
10. Summary
Bài viết này tổng hợp các cách thực tế để truy xuất, định dạng, tính toán và quản lý thời gian hiện tại trong MySQL.
Cuối cùng, đây là những kiến thức tối thiểu bạn cần biết để an toàn.
10.1 Getting the current time (basics)
- Truy xuất chung →
NOW() - DEFAULT bảng / theo dõi cập nhật →
CURRENT_TIMESTAMP - Chỉ ngày →
CURDATE() - Chỉ thời gian →
CURTIME() - UTC →
UTC_TIMESTAMP() - Độ chính xác cao →
NOW(6)
Nguyên tắc chung
- Nếu không chắc, việc sử dụng
NOW()là ổn trong hầu hết các trường hợp. - Đối với thiết kế schema, sử dụng
CURRENT_TIMESTAMP.
10.2 Range queries: “>= AND <” is the golden rule
Mẫu an toàn:
WHERE created_at >= 'START'
AND created_at < 'END'
Tại sao:
- Ngăn ngừa việc bỏ sót các hàng ở ranh giới cuối
- Hoạt động với microgiây
- Giữ chỉ mục có thể sử dụng được
Ví dụ sai
WHERE DATE(created_at) = CURDATE();
Không bao bọc cột trong hàm.
10.3 Datetime arithmetic basics
- Cộng/trừ →
INTERVAL - Hiệu ngày →
DATEDIFF() - Hiệu thời gian →
TIMESTAMPDIFF()
Luôn nhớ rằng cơ sở của bạn là “now” hay “today 00:00.”
10.4 Time zone design principles
- Lưu trữ ở UTC
- Chuyển đổi khi hiển thị
- Đồng nhất chính sách giữa ứng dụng và CSDL
Kiểm tra bằng:
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
- Thiết bị chuẩn cho việc kiểm toán/ghi log/theo dõi cập nhật
- Nếu cần độ chính xác, chỉ định
(6)
4 điểm thực tế quan trọng nhất
- Đừng nhầm lẫn sự khác nhau giữa
NOW()vàCURRENT_TIMESTAMP - Đối với truy vấn phạm vi, sử dụng
>= AND < - Lưu trữ ở UTC theo mặc định
- Quyết định kiểu dữ liệu (DATETIME vs TIMESTAMP) trong giai đoạn thiết kế
Xử lý thời gian hiện tại trong MySQL là nền tảng cho việc ghi log, tổng hợp doanh thu, quản lý xác thực/phiên, công việc batch, và nhiều hơn nữa.
Nếu bạn tuân theo các nguyên tắc trong bài viết này, bạn có thể tránh được nhiều vấn đề phổ biến như trôi thời gian, lỗi ranh giới và giảm hiệu năng.


