查詢不區分大小寫的重複資料是常見需求,但直觀的子查詢寫法在大數據量下效能極差。本文深度解析效能瓶頸,提供臨時表+索引的優化方案,實現數量級提速。
-- ❌ 效能極差,十萬級資料可能跑死資料庫
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;
UPPER(Source) 導致無法使用索引,全表掃描UPPER()CREATE TABLE staticcatalogue_tmp AS
SELECT UPPER(Source) AS Source
FROM staticcatalogue
GROUP BY UPPER(Source)
HAVING COUNT(UPPER(Source)) > 1;
ALTER TABLE staticcatalogue_tmp ADD INDEX idx_source (Source);
SELECT s.*
FROM staticcatalogue s
WHERE UPPER(s.Source) IN (SELECT st.Source FROM staticcatalogue_tmp st)
ORDER BY UPPER(s.Source) DESC;
| 階段 | 原始方案 | 優化方案 | 提升倍數 |
|---|---|---|---|
| 分組去重 | 子查詢每次全表掃描+UPPER計算 | 一次性建表,持久化結果 | 10-100x |
| 關聯查詢 | IN 子查詢,無索引 | 臨時表有索引,ref/eq_ref 連接 | 100-1000x |
| 排序 | ORDER BY UPPER() 無索引 | 同樣問題,但資料量已大幅減少 | 10x |
| 總體 | 十萬級 10 分鐘+ / 百萬級跑死 | 秒級完成 | 數量級 |
若 Source 欄位本身區分大小寫,可考慮:
-- 方案 A:功能性索引 (MySQL 8.0+)
ALTER TABLE staticcatalogue ADD INDEX idx_upper_source ((UPPER(Source)));
-- 方案 B:新增冗餘欄位 source_upper,觸發器維護
ALTER TABLE staticcatalogue ADD COLUMN source_upper VARCHAR(255);
-- INSERT/UPDATE 觸發器 SET NEW.source_upper = UPPER(NEW.Source)
-- 然後直接查 WHERE source_upper IN (...) 可用索引
-- 避免單次大事務鎖表
DELETE FROM staticcatalogue
WHERE id IN (
SELECT id FROM staticcatalogue
WHERE UPPER(Source) IN (SELECT Source FROM staticcatalogue_tmp)
LIMIT 10000
);
TRIM() 同理REPLACE()、SUBSTRING()tmp_ 或 _tmp,避免衝突DROP TEMPORARY TABLE 或定時清理CREATE TEMPORARY TABLES 權限CREATE TABLE ... LIKE ... 而非 SELECT * 避免複製資料「將計算前置、結果持久化、關鍵路徑加索引」 — 這是解決「WHERE 條件用函數導致索引失效」的通用模式,適用於 MySQL、PostgreSQL、SQL Server 等所有關聯式資料庫。