需求:在 staticcatalogue 表中查詢 Source 欄位不分大小寫的重複值。原始 SQL 使用子查詢 + UPPER(),十萬筆資料需 10 分鐘,百萬筆直接拖垮資料庫。
SELECT * FROM staticcatalogue
WHERE UPPER(Source) IN (
SELECT UPPER(Source) FROM staticcatalogue
GROUP BY UPPER(Source) HAVING COUNT(UPPER(Source)) > 1
)
ORDER BY UPPER(Source) DESC;
效能殺手:
WHERE / GROUP BY / ORDER BY 中重複呼叫 UPPER(),導致無法使用索引 (函數索引 MySQL 8.0 前不支援)。-- 1. 建立臨時表,只保留大寫後的重複 Source CREATE TABLE staticcatalogue_tmp SELECT UPPER(Source) AS Source FROM staticcatalogue GROUP BY UPPER(Source) HAVING COUNT(UPPER(Source)) > 1; -- 2. 為臨時表加索引 (關鍵!) ALTER TABLE staticcatalogue_tmp ADD INDEX idx_source (Source); -- 3. 主查詢關聯臨時表 SELECT s.* FROM staticcatalogue s WHERE UPPER(s.Source) IN (SELECT st.Source FROM staticcatalogue_tmp st) ORDER BY UPPER(s.Source) DESC;
Source 欄位已為大寫且有索引,IN (SELECT ...) 可高效執行 Semi-Join。source_upper VARCHAR(...) GENERATED ALWAYS AS (UPPER(Source)) STORED 並建索引 (MySQL 5.7+ 支援),徹底消除函數調用。| 方案 | 執行時間 | 備註 |
|---|---|---|
| 原始子查詢 + UPPER() | > 30 分鐘 / 逾時 | 全表掃描 × 多次 |
| 臨時表 + 索引 | ~ 15 秒 | 提升 > 100 倍 |
| 虛擬欄位 + 索引 (最佳) | ~ 2 秒 | 避免運行時函數,索引完全命中 |
ENGINE=MEMORY (資料量小) 或 ENGINE=InnoDB (資料量大),避免磁碟 I/O 瓶頸。CREATE TEMPORARY TABLE (Session 級自動清理)。CREATE INDEX idx_source_upper ON staticcatalogue ((UPPER(Source))); 免去臨時表步驟。