-- 失效場景
SELECT * FROM dept WHERE dept_id <> 1000; -- 即使有 dept_id 索引,仍 FTS
-- 破解:改寫為範圍 OR 條件 (利用索引範圍掃描)
SELECT * FROM dept WHERE dept_id < 1000 OR dept_id > 1000;
-- 或使用 UNION ALL (避免重複排序)
SELECT * FROM dept WHERE dept_id < 1000
UNION ALL
SELECT * FROM dept WHERE dept_id > 1000;
原理:優化器無法通過「不等於」定位索引範圍,只能掃描全表。範圍 OR 條件可轉為兩次索引範圍掃描合併。
-- 失效場景
SELECT * FROM emp WHERE commission IS NULL; -- B-Tree 索引不存全 NULL 行,FTS
-- 破解方案:
-- 1. 建表時定義 NOT NULL (業務允許)
ALTER TABLE emp MODIFY commission DEFAULT 0 NOT NULL; -- 0 替代 NULL,索引生效
-- 2. 創建函數索引 (FBI) 包含 NULL
CREATE INDEX idx_emp_comm_nvl ON emp(NVL(commission, -1));
SELECT * FROM emp WHERE NVL(commission, -1) = -1; -- 索引生效
-- 3. 位圖索引 (天然包含 NULL,適合低基數列)
CREATE BITMAP INDEX bmx_emp_comm ON emp(commission);
SELECT * FROM emp WHERE commission IS NULL; -- 位圖索引生效
核心知識:B-Tree 索引**不存儲完全為 NULL 的鍵值**,故 `IS NULL` 無法走索引;位圖索引**存儲 NULL**,適合低基數列 (性別、狀態、類型) 的 NULL 判斷。
-- 失效場景
SELECT * FROM emp WHERE UPPER(ename) = 'SMITH'; -- 函數
SELECT * FROM emp WHERE sal * 12 > 50000; -- 運算
SELECT * FROM emp WHERE dept_id = '10'; -- 隱式轉換 (dept_id NUMBER, '10' VARCHAR2)
SELECT * FROM emp WHERE TRUNC(hiredate) = DATE '2023-01-01'; -- 函數
-- 破解方案:
-- 1. 函數索引 (FBI) / 虛擬列索引 (12c+)
CREATE INDEX idx_emp_upper_ename ON emp(UPPER(ename));
CREATE INDEX idx_emp_annual_sal ON emp(sal * 12);
-- 虛擬列 (12c+)
ALTER TABLE emp ADD (annual_sal GENERATED ALWAYS AS (sal * 12) VIRTUAL);
CREATE INDEX idx_emp_annual_sal ON emp(annual_sal);
-- 2. 條件改寫 (將函數/運算移至常量側)
SELECT * FROM emp WHERE hiredate >= DATE '2023-01-01' AND hiredate < DATE '2023-01-02'; -- 範圍查詢走索引
SELECT * FROM emp WHERE dept_id = 10; -- 顯式類型匹配
-- 3. 避免隱式轉換:應用層綁定變量類型與列定義一致
-- 索引:CREATE INDEX idx_emp_dept_job ON emp(dept_id, job_id);
-- 失效場景
SELECT * FROM emp WHERE job_id = 'CLERK'; -- 未包含前導列 dept_id,索引失效 (除非 INDEX SKIP SCAN 10g+)
-- 破解:
-- 1. 調整索引列順序:高選擇性/等值列優先
-- WHERE dept_id=10 AND job_id='CLERK' → 索引 (dept_id, job_id) 最優
-- WHERE job_id='CLERK' AND dept_id=10 → 索引 (job_id, dept_id) 最優
-- 2. 創建單獨索引或額外索引
CREATE INDEX idx_emp_job ON emp(job_id);
-- 3. INDEX SKIP SCAN (10g+ 自動) 適用於前導列基數極低 (如性別 M/F) 時
| 索引類型 | 創建語法 | 核心特性 | 適用場景 | 不適用場景 |
|---|---|---|---|---|
| B-Tree (標準) | `CREATE INDEX idx ON t(col);` | 平衡樹、高基數、等值/範圍/排序、OLTP 核心 | 主鍵、唯一鍵、高選擇性列、外鍵、排序列、範圍查詢 | 低基數列 (性別)、全表掃描更優、大量 DML 維護開銷大 |
| B-Tree 唯一 | `CREATE UNIQUE INDEX idx ON t(col);` | 強制唯一、優化器信任基數=1 | 主鍵、業務唯一鍵 (UK) | 非唯一業務列 |
| 反向鍵索引 | `CREATE INDEX idx ON t(col) REVERSE;` | 鍵值字節反轉、消除右側葉塊熱點 (序列/時間列) | 序列主鍵、時間戳列高併發 INSERT、RAC 環境 | 範圍掃描 (BETWEEN、>、<、ORDER BY) — **完全不支持** |
| 降序索引 | `CREATE INDEX idx ON t(col DESC);` | 鍵值降序存儲、避免 SORT ORDER BY DESC | 頻繁 `ORDER BY col DESC`、Top-N 查詢 `WHERE rownum < 10 ORDER BY col DESC` | 無降序需求場景 |
| 函數索引 (FBI) | `CREATE INDEX idx ON t(UPPER(col));` | 索引存儲函數結果、解決 WHERE 函數列失效 | `WHERE UPPER(col)=...`, `WHERE TRUNC(date)=...`, `WHERE NVL(col,0)=...` | 函數非確定性、大量 DML 維護開銷 |
| 位圖索引 | `CREATE BITMAP INDEX idx ON t(col);` | 位圖結構、極佳壓縮、天然包含 NULL、位運算合並 (AND/OR/NOT) | **低基數列** (性別、狀態、類型、地區)、**數據倉庫/OLAP/DSS**、只讀/批量載入、星型查詢多維過濾 | **OLTP 高併發 DML** (鎖升級、死鎖風險極高)、高基數列 |
| 位圖連接索引 | `CREATE BITMAP INDEX idx ON t(dim_col) FROM t, dim WHERE t.dim_id=dim.id;` | 預連接維度表、星型查詢消除運行時連接 | 數據倉庫星型模型、事實表大、維度表小 | OLTP、頻繁 DML |
| 域索引 | `CREATE INDEX idx ON t(col) INDEXTYPE IS ctxsys.context;` | 擴展索引類型 (Oracle Text, Spatial, XML, JSON) | 全文檢索、空間查詢、XML/JSON 結構化查詢 | 標準關系查詢 |
| 局部/全局分區索引 | `CREATE INDEX idx ON t(col) LOCAL;` / `GLOBAL PARTITION BY RANGE...` | 對齊表分區 (Local) / 跨分區 (Global) | 大表分區環境、分區剪枝、分區維護 (TRUNCATE/DROP PARTITION) | 非分區表 |
, <, >=, <=, LIKE 'prefix%', BETWEEN) → 放等值列之後
3. 排序列 (ORDER BY) → 放範圍列之後 (若範圍列選擇性極高可前置)
4. 低選擇性列 (如狀態) → 盡量不放入索引,或放最後
實戰案例:
WHERE dept_id = 10 -- 等值
AND status = 'ACTIVE' -- 等值 (低基數)
AND hiredate > '2023-01-01' -- 範圍
ORDER BY sal DESC; -- 排序
最優索引列順序:
(dept_id, status, hiredate, sal DESC)
或 (dept_id, hiredate, status, sal DESC) -- 若 hiredate 選擇性極高
-- 1. 開啟索引使用監控 (11g+ 推薦統一審計替代)
ALTER INDEX idx_emp_name MONITORING USAGE;
-- 2. 定期檢查 (如每周)
SELECT io.name index_name, ou.start_monitoring, ou.end_monitoring, ou.used
FROM sys.object_usage ou
JOIN dba_objects io ON ou.obj# = io.object_id
WHERE io.owner = 'SCHEMA_CORE' AND io.object_type = 'INDEX';
-- 3. 長期未用 (used='NO' 且監控期 > 30 天) → 評估刪除
-- 注意:外鍵索引、唯一約束索引、主鍵索引不可刪除
-- 刪除前驗證:SQL Tuning Advisor / SPM 計劃依賴 / 應用代碼搜索
-- 4. 碎片化檢測 (高度/葉塊/聚簇因子)
ANALYZE INDEX idx_emp_name VALIDATE STRUCTURE;
SELECT height, lf_rows, lf_blks, br_rows, br_blks, del_lf_rows, pct_used
FROM index_stats;
-- height > 4 或 pct_used < 50% 或 del_lf_rows/lf_rows > 20% → 考慮 REBUILD/COALESCE
-- 5. 在線重建 (生產無停機)
ALTER INDEX idx_emp_name REBUILD ONLINE TABLESPACE app_index COMPRESS ADVANCED LOW;
-- 或合併碎片 (輕量)
ALTER INDEX idx_emp_name COALESCE;