MySQL DATE_FORMAT() 函數完全指南:按月/按周彙總統計實戰
`DATE_FORMAT(date, format)` 是 MySQL 日期格式化與提取的核心函數,廣泛用於報表統計、時間維度分組。本文整理完整格式符號表與實戰範例。
完整格式符號對照表
| 符號 | 說明 | 範例輸出 |
| %a | 縮寫星期名 | Mon, Tue |
| %b | 縮寫月名 | Jan, Feb |
| %c | 月份數值 | 1-12 |
| %D | 帶英文前綴的日期 | 1st, 2nd, 3rd |
| %d | 月份中的天 (兩位) | 01-31 |
| %e | 月份中的天 (不補零) | 1-31 |
| %f | 微秒 (六位) | 000000-999999 |
| %H | 小時 (24制, 兩位) | 00-23 |
| %h / %I | 小時 (12制, 兩位) | 01-12 |
| %i | 分鐘 (兩位) | 00-59 |
| %j | 年份中的天 (三位) | 001-366 |
| %k | 小時 (24制, 不補零) | 0-23 |
| %l | 小時 (12制, 不補零) | 1-12 |
| %M | 完整月名 | January, February |
| %m | 月份 (兩位) | 01-12 |
| %p | AM/PM | AM, PM |
| %r | 12制時間 (hh:mm:ss AM/PM) | 11:30:45 PM |
| %S / %s | 秒 (兩位) | 00-59 |
| %T | 24制時間 (hh:mm:ss) | 23:30:45 |
| %U | 周數 (00-53), 週日為週首 | 01, 52 |
| %u | 周數 (00-53), 週一為週首 ISO 8601 常用 | 01, 52 |
| %V | 周數 (01-53), 週日週首, 配合 %X | 01-53 |
| %v | 周數 (01-53), 週一週首, 配合 %x ISO 8601 常用 | 01-53 |
| %W | 完整星期名 | Monday, Tuesday |
| %w | 星期索引 (0=週日, 6=週六) | 0-6 |
| %X | 年份 (四位), 週日週首, 配合 %V | 2024 |
| %x | 年份 (四位), 週一週首, 配合 %v | 2024 |
| %Y | 年份 (四位) | 2024 |
| %y | 年份 (兩位) | 24 |
常用格式化範例
SELECT DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i:%s'); -- 2024-01-15 14:30:45
SELECT DATE_FORMAT(NOW(), '%Y%m%d'); -- 20240115 (檔名用)
SELECT DATE_FORMAT(NOW(), '%b %d, %Y'); -- Jan 15, 2024
SELECT DATE_FORMAT(NOW(), '%W, %M %d, %Y'); -- Monday, January 15, 2024
實戰:按周/按月彙總統計
按周統計 (ISO 8601 週一為週首)
SELECT
DATE_FORMAT(`order_create_time`, '%x-%v') AS year_week, -- 2024-03
SUM(`total_amount`) AS total_sales,
COUNT(*) AS order_count
FROM `orders`
WHERE `order_create_time` >= '2024-01-01'
GROUP BY year_week
ORDER BY year_week;
結果範例:
| year_week | total_sales | order_count |
| 2024-01 | 854314.83 | 1250 |
| 2024-02 | 5177385.26 | 4320 |
| 2024-03 | 4870063.87 | 3980 |
按月統計
SELECT
DATE_FORMAT(`order_create_time`, '%Y-%m') AS year_month,
SUM(`total_amount`) AS total_sales
FROM `orders`
WHERE YEAR(`order_create_time`) = 2024
GROUP BY year_month
ORDER BY year_month;
按日統計
SELECT
DATE_FORMAT(`order_create_time`, '%Y-%m-%d') AS day,
SUM(`total_amount`) AS daily_sales
FROM `orders`
WHERE `order_create_time` BETWEEN '2024-01-01' AND '2024-01-31'
GROUP BY day
ORDER BY day;
效能優化關鍵點
| 問題 | 原因 | 解決 |
| DATE_FORMAT 導致全表掃描 | 函數作用於欄位,索引失效 | 建立函數索引 (8.0+) 或虛擬欄位+索引 |
| 大表 GROUP BY 慢 | 臨時表/Filesort | 分區表(按時間)、預聚合表、OLAP 引擎 |
| 跨年周數錯誤 | %U/%u 與 %Y 不匹配 | 嚴格使用 %x-%v 或 %X-%V 組合 |
虛擬欄位索引優化範例 (5.7+)
ALTER TABLE `orders`
ADD COLUMN `order_year_week` VARCHAR(7)
GENERATED ALWAYS AS (DATE_FORMAT(`order_create_time`, '%x-%v')) VIRTUAL,
ADD INDEX `idx_year_week` (`order_year_week`);
-- 查詢自動命中索引
SELECT `order_year_week`, SUM(`total_amount`)
FROM `orders`
WHERE `order_year_week` BETWEEN '2024-01' AND '2024-12'
GROUP BY `order_year_week`;
常見陷阱
- ❌ `WHERE DATE_FORMAT(date, '%Y-%m') = '2024-01'` → 全表掃描
- ✅ `WHERE date >= '2024-01-01' AND date < '2024-02-01'` → 範圍查詢命中索引
- ❌ `%u` (週一週首) 搭配 `%Y` → 跨年周歸屬錯誤
- ✅ 統一使用 `%x-%v` (ISO 8601: 年-周,週一週首)