大表 (1000-3000 萬行) 按日期查詢:
-- 寫法導致全表掃描、索引失效
SELECT * FROM orders
WHERE TO_CHAR(create_time, 'YYYY-MM-DD') = '2024-01-15';
後果:每行都要執行函數轉換,無法使用 create_time 索引,內存溢出、查詢極慢。
核心思想:**對常數做轉換,而非對欄位做轉換**。
-- 目標:查 2024-01-15 全天資料
-- 錯誤
WHERE TO_CHAR(create_time, 'YYYY-MM-DD') = '2024-01-15'
-- 正確:常數轉日期範圍
WHERE create_time >= TO_DATE('2024-01-15', 'YYYY-MM-DD')
AND create_time < TO_DATE('2024-01-16', 'YYYY-MM-DD')
-- 或包含當天 23:59:59
WHERE create_time <= TO_DATE('2024-01-15 23:59:59', 'YYYY-MM-DD HH24:MI:SS')
-- 目標:查 2024-01-15 全天資料
-- 錯誤
WHERE DATE(create_time) = '2024-01-15'
-- 或
WHERE TO_CHAR(create_time, '%Y-%m-%d') = '2024-01-15' -- MySQL 無 TO_CHAR,用 DATE_FORMAT
-- 正確:範圍查詢
WHERE create_time >= '2024-01-15'
AND create_time < '2024-01-16'
-- 或用 STR_TO_DATE 組合常數
WHERE create_time <= STR_TO_DATE(CONCAT('2024-01-15', ' 23:59:59'), '%Y-%m-%d %H:%i:%s')
-- 參數化查詢 (應用層組裝)
-- 查詢某一天
WHERE dt >= ? AND dt < ? + INTERVAL 1 DAY
-- 綁定參數: '2024-01-15', '2024-01-15'
-- 查詢某月
WHERE dt >= ? AND dt < ? + INTERVAL 1 MONTH
-- 綁定參數: '2024-01-01', '2024-01-01'
-- 建立測試表 1000 萬行
CREATE TABLE t (id INT PRIMARY KEY, dt DATETIME, KEY idx_dt(dt));
-- 方案 1: 函數法
EXPLAIN SELECT * FROM t WHERE DATE(dt) = '2024-01-15';
-- type=ALL, rows=10000000
-- 方案 2: 範圍法
EXPLAIN SELECT * FROM t WHERE dt >= '2024-01-15' AND dt < '2024-01-16';
-- type=ref, rows=約幾百/幾千 (視資料分布)
若表按日期分區 (RANGE COLUMNS),範圍查詢會自動 **Partition Pruning**,只掃描相關分區,效能更強。
「欄位不加工,常數來轉換;範圍查詢走索引,函數查詢全表掃。」