日期比較是 SQL 查詢高頻操作,寫法優劣直接決定索引使用與查詢效能。本文系統對比多種比較模式,提供索引友好的最佳實踐模式。
SELECT * FROM `tb`
WHERE `c` > DATE_FORMAT('2007-07-06', '%Y%m%d')
AND `c` <= DATE_FORMAT('2007-07-09', '%Y%m%d');
⚠️ 對欄位 `c` 使用函數導致索引失效,全表掃描。
SELECT * FROM `tb`
WHERE `c` > DATE('2007-07-07') AND `c` < DATE('2007-07-09');
同樣對欄位隱性轉換,無法使用索引。
-- 儲存格式為 'YYYYMMDD' 字串時可用
SELECT * FROM `LOGINSTAT`
WHERE `STATDAY` > DATE_FORMAT(CURDATE()-1, '%Y%m%d')
AND `STATDAY` >= DATE_FORMAT(CURDATE(), '%Y%m%d');
若欄位為 CHAR/VARCHAR 且格式固定,可走索引;但極易因格式不一致產生 Bug。
-- DATETIME/TIMESTAMP/DATE 欄位直接比較
SELECT * FROM `orders`
WHERE `order_date` >= '2023-01-01'
AND `order_date` < '2023-02-01'; -- 左閉右開區間,含 1 月全天
優勢:SARGable(可用索引範圍掃描),優化器可精準估算行數。
-- 儲存為 INT/UNSIGNED INT 的 Unix 時間戳
SELECT * FROM `logs`
WHERE `create_time` > UNIX_TIMESTAMP('2023-01-01 00:00:00')
AND `create_time` < UNIX_TIMESTAMP('2023-02-01 00:00:00');
-- 動態範圍:最近 24 小時
SELECT * FROM `logs`
WHERE `create_time` > UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 1 DAY));
整數比較極快,但需應用層統一時間戳存儲標準。
// Java 端正確獲取完整時間戳
Timestamp ts = rs.getTimestamp("pub_time"); // 含時分秒毫秒
Date time1 = new Date(ts.getTime());
SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String pubTime = fmt.format(time1);
// SQL 參數化查詢(防注入 + 類型安全)
String sql = "SELECT * FROM do_document WHERE pub_time < ? ORDER BY pub_time DESC LIMIT 1";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setTimestamp(1, ts); // 直接傳 Timestamp 物件,驅動自動處理格式
ResultSet rs2 = ps.executeQuery();
⚠️ 原文拼接字串方式極易注入且格式脆弱,強制使用 PreparedStatement。
-- 包含邊界:2003-01-01 00:00:00 到 2003-12-31 23:59:59
SELECT * FROM `orders`
WHERE `order_date` BETWEEN '2003-01-01' AND '2003-12-31 23:59:59';
-- 更安全的左閉右開寫法(避免秒級邊界遺漏)
SELECT * FROM `orders`
WHERE `order_date` >= '2003-01-01'
AND `order_date` < '2004-01-01';
SELECT * FROM `orders`
WHERE YEAR(`order_date`) = 2007
AND MONTH(`order_date`) = 6
AND DAY(`order_date`) = 10;
優化改寫:
SELECT * FROM `orders`
WHERE `order_date` >= '2007-06-10'
AND `order_date` < '2007-06-11';
-- 今天
WHERE `dt` >= CURDATE() AND `dt` < DATE_ADD(CURDATE(), INTERVAL 1 DAY)
-- 昨天
WHERE `dt` >= DATE_SUB(CURDATE(), INTERVAL 1 DAY) AND `dt` < CURDATE()
-- 本月
WHERE `dt` >= DATE_FORMAT(CURDATE(), '%Y-%m-01')
AND `dt` < DATE_ADD(DATE_FORMAT(CURDATE(), '%Y-%m-01'), INTERVAL 1 MONTH)
-- 最近 30 天
WHERE `dt` >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)
col >= start AND col < end_next 替代 BETWEEN 或 <=| 寫法 | 耗時 | 掃描行數 | 索引使用 |
|---|---|---|---|
| WHERE DATE(order_date) = '2023-01-01' | 1.2s | 1,000,000 | 全表掃描 |
| WHERE order_date >= '2023-01-01' AND order_date < '2023-01-02' | 3ms | 1,245 | 範圍掃描 (ref/range) |
| WHERE UNIX_TIMESTAMP(order_date) > 1672502400 | 1.1s | 1,000,000 | 全表掃描 |
| WHERE order_date > FROM_UNIXTIME(1672502400) | 4ms | 1,245 | 範圍掃描 |
日期比較的本質是將業務時間邏輯映射為索引可用的範圍條件。堅持「欄位不加工、常量承擔轉換」原則,是 SQL 效能優化最高性價比的動作。