萬盛學電腦網

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

SQL 日期查詢優化:避免 TO_CHAR/TO_DATE 導致全表掃描

問題場景

大表 (1000-3000 萬行) 按日期查詢:

-- 寫法導致全表掃描、索引失效
SELECT * FROM orders 
WHERE TO_CHAR(create_time, 'YYYY-MM-DD') = '2024-01-15';

後果:每行都要執行函數轉換,無法使用 create_time 索引,內存溢出、查詢極慢。

優化原則:將條件轉換為「欄位範圍查詢」

核心思想:**對常數做轉換,而非對欄位做轉換**。

Oracle 寫法

-- 目標:查 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')

MySQL 寫法

-- 目標:查 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**,只掃描相關分區,效能更強。

總結口訣

「欄位不加工,常數來轉換;範圍查詢走索引,函數查詢全表掃。」

copyright © 萬盛學電腦網 all rights reserved