Oracle 預設區分大小寫,若需實現大小寫不敏感查詢,常見做法有三種:轉換查詢值、轉換欄位值、雙向轉換。本文以實測資料對比三種寫法的結果差異、索引使用情況與執行計畫成本,幫助您根據業務場景選擇最佳方案。
CREATE TABLE t_vvip (name VARCHAR2(10));
INSERT INTO t_vvip VALUES ('KKK');
INSERT INTO t_vvip VALUES ('kkk');
INSERT INTO t_vvip VALUES ('KkK');
INSERT INTO t_vvip VALUES ('kKk');
COMMIT;
共 4 筆資料,大小寫排列組合各異。
SELECT * FROM t_vvip
WHERE name = LOWER('KkK') -- 'kkk'
OR name = UPPER('KkK'); -- 'KKK'
結果: 僅回傳 'KKK'、'kkk' 兩筆。
缺陷: 資料表中 'KkK'、'kKk' 混合大小寫值無法匹配,查詢邏輯不完整。
SELECT * FROM t_vvip WHERE LOWER(name) = 'KkK'; -- 0 筆
SELECT * FROM t_vvip WHERE UPPER(name) = 'KkK'; -- 0 筆
結果: 0 筆。
缺陷: 欄位轉為全大/小寫後,與混合大小寫的查詢值 'KkK' 永不相等。
SELECT * FROM t_vvip
WHERE LOWER(name) = LOWER('KkK');
-- 或
WHERE UPPER(name) = UPPER('KkK');
結果: 4 筆全回傳 ✅
原理: 將比較雙方統一至同一大小寫基準,語意等同於 COLLATE 不敏感比較。
CREATE INDEX idx_vvip_name ON t_vvip(name);
EXPLAIN PLAN FOR
SELECT * FROM t_vvip WHERE LOWER(name) = LOWER('KkK');
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
-- 計畫顯示:FULL TABLE SCAN (索引未被使用)
| 方案 | 建立方式 | 查詢寫法 | 索引使用 | 維護成本 |
|---|---|---|---|---|
| 函數索引 (Function-Based Index) | CREATE INDEX idx_low_name ON t_vvip(LOWER(name)); | WHERE LOWER(name) = LOWER(:val) | ✅ RANGE SCAN | 低 (自動維護) |
| 虛擬欄位 + 索引 (11g+) | ALTER TABLE t_vvip ADD (name_lower AS (LOWER(name))); CREATE INDEX idx_vname_lower ON t_vvip(name_lower); | WHERE name_lower = LOWER(:val) | ✅ RANGE SCAN | 低 (可見欄位,易除錯) |
| 語言索引 (NLS_SORT) | CREATE INDEX idx_nls ON t_vvip(NLSSORT(name, 'NLS_SORT=BINARY_CI')); | WHERE NLSSORT(name, 'NLS_SORT=BINARY_CI') = NLSSORT(:val, 'NLS_SORT=BINARY_CI') | ✅ | 中 (需 Session 設定一致) |
| 應用層統一大小寫 | 寫入時統一 UPPER() | WHERE name = UPPER(:val) | ✅ 一般索引 | 高 (需改造所有寫入路徑) |
-- 12cR2 引入 COLLATE 子句
SELECT * FROM t_vvip
WHERE name = 'KkK' COLLATE BINARY_CI; -- CI = Case Insensitive
此語法不需函數索引,直接利用 BINARY_CI 定序比較,效能最佳且語意清晰 (需資料庫字元集支援)。
COLLATE BINARY_CI (12cR2+) 或虛擬欄位 + 索引 (11g+)。CREATE INDEX ... ON t(LOWER(col)),SQL 改為 LOWER(col) = LOWER(:val)。大小寫不敏感查詢看似簡單,實則關聯索引效能、資料正確性與遷移相容性。選對策略,才能兼顧查詢正確與執行效率。