Hôm nay tụi mình lại đào sâu vào việc thao tác dữ liệu thời gian, học cách lấy ra các phần cụ thể (ví dụ như năm, tháng hoặc thứ trong tuần) bằng hàm EXTRACT() và tìm hiểu cách tính tuổi hoặc khoảng thời gian giữa hai ngày với hàm AGE().
Làm việc với dữ liệu thời gian trong các dự án thực tế thường cần phải tách lấy các phần cụ thể của ngày hoặc giờ. Ví dụ:
- Phân chia đơn hàng theo năm hoặc tháng;
- Đếm số lượng người dùng đăng ký vào một ngày cụ thể trong tuần;
- Phân tích độ dài thời gian giữa hai sự kiện.
Để giải quyết mấy bài toán này, tụi mình dùng các hàm EXTRACT() và AGE().
EXTRACT() là gì?
Hàm EXTRACT() cho phép lấy ra các phần cụ thể của ngày hoặc timestamp. Ví dụ, bạn có thể lấy năm từ ngày sinh, xác định số tháng hoặc thậm chí lấy thứ trong tuần.
Cú pháp:
EXTRACT(part FROM source)
part: phần của ngày mà bạn muốn lấy. Có thể làYEAR,MONTH,DAY,HOUR,MINUTE,SECONDvà các loại khác.source: kiểu dữ liệu thời gian mà bạn muốn lấy thông tin. Có thể là cột, hằng số hoặc kết quả của một hàm.
Ví dụ 1: lấy năm, tháng và ngày
SELECT
EXTRACT(YEAR FROM '2024-11-15'::DATE) AS year_part,
EXTRACT(MONTH FROM '2024-11-15'::DATE) AS month_part,
EXTRACT(DAY FROM '2024-11-15'::DATE) AS day_part;
Kết quả:
| year_part | month_part | day_part |
|---|---|---|
| 2024 | 11 | 15 |
Ở đây tụi mình đã lấy ra năm, tháng và ngày từ ngày 2024-11-15. Cách này rất hữu ích khi cần group dữ liệu theo từng phần của ngày.
Ví dụ 2: thứ trong tuần và giờ từ thời gian
SELECT
EXTRACT(DOW FROM '2024-11-15'::DATE) AS day_of_week,
EXTRACT(HOUR FROM '15:30:00'::TIME) AS hour_part;
Kết quả:
| day_of_week | hour_part |
|---|---|
| 3 | 15 |
DOW(Day of Week) trả về số thứ trong tuần: chủ nhật —0, thứ hai —1, v.v.HOURlấy ra giờ từ thời gian.
Ví dụ 3: áp dụng cho cột
Nếu tụi mình có một bảng chứa các ngày, có thể lấy từng phần của ngày để phân tích. Giả sử có bảng orders:
| order_id | order_date |
|---|---|
| 1 | 2023-05-12 14:20 |
| 2 | 2023-06-18 10:45 |
| 3 | 2023-07-22 21:15 |
SELECT
order_id,
EXTRACT(MONTH FROM order_date) AS month,
EXTRACT(DAY FROM order_date) AS day
FROM orders;
Kết quả:
| order_id | month | day |
|---|---|---|
| 1 | 5 | 12 |
| 2 | 6 | 18 |
| 3 | 7 | 22 |
AGE() là gì?
Hàm AGE() dùng để tính khoảng cách giữa hai timestamp. Ví dụ, nó giúp tính tuổi khách hàng dựa trên ngày sinh hoặc xác định đã bao lâu kể từ khi đặt hàng.
Cú pháp:
AGE(timestamp1, timestamp2)
timestamp1: timestamp muộn hơn.timestamp2: timestamp sớm hơn.- Nếu chỉ truyền một tham số, PostgreSQL sẽ tự động so sánh với ngày hiện tại (
NOW()).
Ví dụ 1: tính tuổi
SELECT AGE('2025-11-15'::DATE, '1990-05-12'::DATE) AS age;
Kết quả:
| age |
|---|
| 35 years 6 mons |
Ví dụ này cho thấy tuổi của một người sinh ngày 12 tháng 5 năm 1990 tính đến ngày 15 tháng 11 năm 2025.
Ví dụ 2: khoảng thời gian giữa các sự kiện
SELECT AGE('2023-06-01 15:00'::TIMESTAMP, '2023-05-20 10:30'::TIMESTAMP) AS duration;
Kết quả:
| duration |
|---|
| 11 days 4:30:00 |
Ở đây tụi mình tính khoảng thời gian giữa hai sự kiện. Rất hữu ích khi cần biết đã bao lâu từ lúc bắt đầu đến khi hoàn thành một task.
Ví dụ 3: tuổi của khách hàng
Giả sử có bảng customers:
| customer_id | birth_date |
|---|---|
| 1 | 1992-03-10 |
| 2 | 1985-07-07 |
Tụi mình có thể tính tuổi khách hàng như sau:
SELECT
customer_id,
AGE(NOW(), birth_date) AS age
FROM customers;
Kết quả vào ngày 13 tháng 6 năm 2025:
| customer_id | age |
|---|---|
| 1 | 33 years 3 mons |
| 2 | 39 years 11 mons |
Dĩ nhiên, bạn sẽ có giá trị NOW() và kết quả riêng của mình.
Ví dụ thực tế dùng EXTRACT() và AGE()
Giờ tụi mình kết hợp các hàm này trong các tình huống thực tế nhé.
Ví dụ 1: group dữ liệu theo tháng
Giả sử có bảng đơn hàng với ngày đặt. Để đếm số đơn theo từng tháng, dùng câu lệnh sau:
SELECT
EXTRACT(MONTH FROM order_date) AS order_month,
COUNT(*) AS total_orders
FROM orders
GROUP BY order_month
ORDER BY order_month;
Ví dụ 2: số ngày còn lại đến hạn
Giả sử có bảng subscriptions:
| subscription_id | expiry_date |
|---|---|
| 1 | 2023-12-31 |
| 2 | 2024-05-15 |
Tụi mình muốn biết còn bao nhiêu ngày đến khi hết hạn:
SELECT
subscription_id,
AGE(expiry_date, NOW()) AS time_remaining
FROM subscriptions;
Kết quả:
| subscription_id | time_remaining |
|---|---|
| 1 | 1 mons 15 days |
| 2 | 6 mons |
Lỗi thường gặp và cách tránh
Khi dùng EXTRACT() và AGE(), mấy bạn mới hay gặp mấy lỗi sau:
- Cố lấy phần không hợp lệ, ví dụ lấy tháng từ kiểu
TIME. Nhớ nha: các phầnYEAR,MONTHvàDAYchỉ dùng vớiDATE,TIMESTAMP, không dùng vớiTIME. - Vấn đề với các định dạng thời gian khác nhau. Ví dụ, chuỗi
2023/11/15không được coi là ngày. Hãy dùng ép kiểu với::DATEhoặcTO_DATE(). - Khác biệt giữa
AGE()và phép trừ timestamp. Nếu bạn cần khoảng thời gian chính xác (tháng, ngày, giây) — dùngAGE(), còn nếu chỉ cần số ngày thì dùng phép toán trừ bình thường.
Giờ thì bạn đã có đủ skill để lấy và phân tích các phần của dữ liệu thời gian trong PostgreSQL rồi đó. Thử nghịch với EXTRACT() và AGE() trong project của bạn đi nhé!
GO TO FULL VERSION