今天我們又要來深入玩玩時間資料,學怎麼用 EXTRACT() 從裡面抓出特定部分(像是年、月或星期幾),還有怎麼用 AGE() 算年齡或兩個日期之間的時間差。
在實際專案裡,處理時間資料常常會需要把日期或時間拆開來用。舉例來說:
- 把訂單依照年份或月份分類;
- 算某一天註冊的用戶數量;
- 分析兩個事件之間的時間長度。
這種需求我們就會用到 EXTRACT() 跟 AGE() 這兩個函數。
什麼是 EXTRACT()?
EXTRACT() 這個函數可以讓你從日期或時間戳裡面抓出你要的部分。比如說,你可以抓出生日的年份、月份,甚至星期幾。
語法:
EXTRACT(part FROM source)
part:你想要抓的日期部分。可以是YEAR、MONTH、DAY、HOUR、MINUTE、SECOND等等。source:你要從哪個時間型別抓資料。可以是欄位、常數或函數的結果。
範例 1:抓出年、月、日
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;
結果:
| year_part | month_part | day_part |
|---|---|---|
| 2024 | 11 | 15 |
這裡我們從 2024-11-15 這個日期抓出年、月、日。這種做法很適合你要依照日期的某個部分來分組資料時用。
範例 2:抓出星期幾跟小時
SELECT
EXTRACT(DOW FROM '2024-11-15'::DATE) AS day_of_week,
EXTRACT(HOUR FROM '15:30:00'::TIME) AS hour_part;
結果:
| day_of_week | hour_part |
|---|---|
| 3 | 15 |
DOW(Day of Week) 會回傳星期幾的編號:星期天是0,星期一是1,以此類推。HOUR則是從時間裡抓出小時。
範例 3:應用在欄位上
如果你有一張有日期的資料表,也可以直接抓出日期的部分來分析。假設有一張 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;
結果:
| order_id | month | day |
|---|---|---|
| 1 | 5 | 12 |
| 2 | 6 | 18 |
| 3 | 7 | 22 |
什麼是 AGE()?
AGE() 這個函數是用來算兩個時間戳之間的差距。比如說,你可以用來算客戶的年齡,或是某個訂單到現在過了多久。
語法:
AGE(timestamp1, timestamp2)
timestamp1:比較晚的那個時間戳。timestamp2:比較早的那個時間戳。- 如果只給一個參數,PostgreSQL 會自動拿現在的時間(
NOW())去比。
範例 1:算年齡
SELECT AGE('2025-11-15'::DATE, '1990-05-12'::DATE) AS age;
結果:
| age |
|---|
| 35 years 6 mons |
這個例子是算一個 1990 年 5 月 12 日出生的人,到 2025 年 11 月 15 日的年齡。
範例 2:事件間的時間間隔
SELECT AGE('2023-06-01 15:00'::TIMESTAMP, '2023-05-20 10:30'::TIMESTAMP) AS duration;
結果:
| duration |
|---|
| 11 days 4:30:00 |
這裡我們算出兩個事件之間的時間間隔。這在你要計算任務開始到結束花了多久時很有用。
範例 3:客戶年齡
假設有一張 customers 表:
| customer_id | birth_date |
|---|---|
| 1 | 1992-03-10 |
| 2 | 1985-07-07 |
我們可以算出客戶的年齡:
SELECT
customer_id,
AGE(NOW(), birth_date) AS age
FROM customers;
2025 年 6 月 13 日的結果:
| customer_id | age |
|---|---|
| 1 | 33 years 3 mons |
| 2 | 39 years 11 mons |
當然,你的 NOW() 跟結果會依照查詢時間不同而變。
EXTRACT() 和 AGE() 的實戰範例
現在來看一下這兩個函數在實際情境下怎麼搭配用。
範例 1:依月份分組資料
假設有一張訂單表,想要統計每個月的訂單數,可以這樣寫:
SELECT
EXTRACT(MONTH FROM order_date) AS order_month,
COUNT(*) AS total_orders
FROM orders
GROUP BY order_month
ORDER BY order_month;
範例 2:到期還剩幾天
假設有一張 subscriptions 表:
| subscription_id | expiry_date |
|---|---|
| 1 | 2023-12-31 |
| 2 | 2024-05-15 |
我們想知道訂閱還剩幾天到期:
SELECT
subscription_id,
AGE(expiry_date, NOW()) AS time_remaining
FROM subscriptions;
結果:
| subscription_id | time_remaining |
|---|---|
| 1 | 1 mons 15 days |
| 2 | 6 mons |
常見錯誤跟怎麼避免
用 EXTRACT() 跟 AGE() 的時候,新手常常會遇到這些小陷阱:
- 想從
TIME型別抓月份這種不合理的部分。記得:YEAR、MONTH、DAY這些只能用在DATE、TIMESTAMP,不能用在TIME。 - 時間資料格式不一樣的問題。像
2023/11/15這種寫法不是日期,記得用::DATE或TO_DATE()轉型。 AGE()跟直接減時間戳的差別。如果你要精確的間隔(幾個月、幾天、幾秒)就用AGE(),如果只想知道天數,直接做減法就好。
現在你已經有所有必要的技能,可以在 PostgreSQL 裡面隨意抓取和分析時間資料的部分了。快去你的專案裡玩玩 EXTRACT() 跟 AGE() 吧!
GO TO FULL VERSION