萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 資料庫 >> MySQL 教學

MySQL 當天/本周/本月/相對時間查詢:DATE()、YEARWEEK()、DATE_FORMAT() 多種寫法對比

查詢「今天的記錄」、「本周數據」、「上個月統計」是業務高頻需求。MySQL 提供多種日期函數組合實現,但效能與正確性差異巨大。本文系統整理各時間顆粒度的標準寫法,並重點對比索引友好與非友好寫法。

核心函數速查

函數用途示例結果
DATE(dt)提取日期部分DATE('2024-01-15 14:30') → '2024-01-15'
CURDATE()當前日期'2024-01-15'
NOW()當前日期時間'2024-01-15 14:30:00'
TO_DAYS(dt)日期轉天數(從 Year 0)738500
YEARWEEK(dt, mode)年周數202403
DATE_FORMAT(dt, fmt)格式化'%Y-%m' → '2024-01'
DATE_SUB(dt, INTERVAL n UNIT)日期減間隔7天前
PERIOD_DIFF(p1, p2)月份差PERIOD_DIFF(202401, 202312)=1

DATETIME/TIMESTAMP 欄位:推薦寫法(索引友好)

今日

-- ✅ 索引友好:範圍查詢
SELECT * FROM t WHERE dt_col >= '2024-01-15 00:00:00' AND dt_col < '2024-01-16 00:00:00';

-- ⚠️ 函數導致索引失效(MySQL 5.6.5+ DATE() 可用索引)
SELECT * FROM t WHERE DATE(dt_col) = CURDATE();

昨日

SELECT * FROM t WHERE dt_col >= DATE_SUB(CURDATE(), INTERVAL 1 DAY) 
                      AND dt_col < CURDATE();

近 7 天 / 近 30 天

SELECT * FROM t WHERE dt_col >= DATE_SUB(CURDATE(), INTERVAL 7 DAY);
SELECT * FROM t WHERE dt_col >= DATE_SUB(CURDATE(), INTERVAL 30 DAY);

本周(模式 3:周一開始)

SELECT * FROM t WHERE YEARWEEK(dt_col, 3) = YEARWEEK(CURDATE(), 3);

上周

SELECT * FROM t WHERE YEARWEEK(dt_col, 3) = YEARWEEK(CURDATE(), 3) - 1;

本月

SELECT * FROM t WHERE dt_col >= DATE_FORMAT(CURDATE(), '%Y-%m-01')
                      AND dt_col < DATE_FORMAT(CURDATE() + INTERVAL 1 MONTH, '%Y-%m-01');

上月

SELECT * FROM t WHERE dt_col >= DATE_FORMAT(DATE_SUB(CURDATE(), INTERVAL 1 MONTH), '%Y-%m-01')
                      AND dt_col < DATE_FORMAT(CURDATE(), '%Y-%m-01');

INT 時間戳欄位:轉換後查詢

-- 今日
SELECT * FROM t WHERE DATE(FROM_UNIXTIME(ts_col)) = CURDATE();

-- 近 7 天
SELECT * FROM t WHERE FROM_UNIXTIME(ts_col) >= DATE_SUB(NOW(), INTERVAL 7 DAY);

秒級精度查詢(近 N 秒)

SELECT COUNT(*), 
       SUM(IF(logusertype=2, 1, 0)) AS type2_cnt,
       SUM(IF(logusertype=3, 1, 0)) AS type3_cnt
FROM testlog 
WHERE UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(logendtime) <= 30;

效能關鍵:避免欄位上用函數

寫法能否用索引建議
WHERE DATE(dt) = CURDATE()MySQL 5.6.5+ 可,舊版不可改用範圍查詢最穩妥
WHERE YEAR(dt)=2024 AND MONTH(dt)=1❌ 不能改用範圍
WHERE DATE_FORMAT(dt, '%Y%m') = '202401'❌ 不能改用範圍
WHERE dt >= '2024-01-01' AND dt < '2024-02-01'✅ 能推薦寫法
WHERE ts >= UNIX_TIMESTAMP('2024-01-01') AND ts < UNIX_TIMESTAMP('2024-02-01')✅ 能INT 時間戳同理

應用層計算邊界值(最佳實踐)

// PHP 範例
$todayStart = (new DateTime())->setTime(0,0,0)->format('Y-m-d H:i:s');
$todayEnd = (clone $todayStart)->modify('+1 day')->format('Y-m-d H:i:s');
$stmt = $pdo->prepare("SELECT * FROM orders WHERE created_at >= ? AND created_at < ?");
$stmt->execute([$todayStart, $todayEnd]);

優勢:SQL 固定可複用執行計劃、無函數依賴、跨時區邏輯集中在應用層。

特殊場景:本周/上周定義差異

YEARWEEK() 第二參數 mode 決定周起始日與第 1 周定義:

Mode周起始第 1 周定義
0Sunday第一個周日
1Monday第一個周一(ISO 8601)
2Sunday第一個有 4+ 天的周
3Monday第一個有 4+ 天的周(ISO 8601)
4-7類似不同第 1 周定義

業務必須確認「本周」定義,統一應用層與資料庫層的 mode。

掌握索引友好的相對時間查詢寫法,是將業務邏輯高效下推至資料庫層的關鍵技能。

copyright © 萬盛學電腦網 all rights reserved