| 操作符/模式 | 問題 | 改寫方案 | 原理 |
|---|---|---|---|
| IN 子查詢 | 轉換為 JOIN 失敗時先執行子查詢,多一次轉換開銷 | 改用 EXISTS / JOIN | 半連接 (SEMI JOIN) 可提前過濾,避免物化子查詢 |
| NOT IN | **含 NULL 時結果為空**,且無法使用索引 | NOT EXISTS / LEFT JOIN ... IS NULL | 反連接 (ANTI JOIN) 可走索引,語義正確 |
| != / <> | 永遠不走索引,全表掃描 | `a > 0 OR a < 0` / `a > ''` (字串) | 將「不等於」拆解為「範圍」讓索引生效 |
| IS NULL / IS NOT NULL | B-Tree 不存 NULL,無法走索引 | 預設值 + CHECK 約束 / Function-Based Index / Bitmap Index | 將 NULL 轉為實際值或用 Bitmap 索引 NULL |
| LIKE '%abc%' | 前導通配符導致全表掃描 | LIKE 'abc%' / Oracle Text / 反向鍵索引 | 前導固定前綴可走索引範圍掃描 |
| UNION | 去重排序開銷大 | UNION ALL (業務確無重複時) | 省略 SORT UNIQUE 操作 |
| >= vs > 常數 | `A >= 3` 直接定位 3;`A > 2` 先定位 2 再過濾 | 優先用 >= / <= 邊界值 | 減少索引掃描起始點比較次數 |
| 阻礙模式 | 範例 | 修正 | 原則 |
|---|---|---|---|
| 函數包裹列 | `SUBSTR(hbs_bh,1,4)='5400'` | `hbs_bh LIKE '5400%'` | 列保持「裸露」,函數移至常數側 |
| 隱式/顯式運算 | `TRUNC(sk_rq)=TRUNC(SYSDATE)` | `sk_rq >= TRUNC(SYSDATE) AND sk_rq < TRUNC(SYSDATE)+1` | 範圍查詢保留索引 |
| 列參與運算 | `ss_df+20 > 50` | `ss_df > 30` | 常數預計算移至右側 |
| 字串拼接比較 | `'X'||hbs_bh > 'X5400021452'` | `hbs_bh > '5400021542'` | 避免列側運算 |
| 隱式類型轉換 | `hbs_bh = 5401002554` (hbs_bh 是 VARCHAR2) | `hbs_bh = '5401002554'` | 顯式轉換常數,避免列側 TO_NUMBER |
| 多列運算 | `ys_df > cx_df` | 無法直接優化,考慮虛擬欄位 + 索引 | 列間運算破壞索引 |
| 多列拼接比較 | `qc_bh||kh_bh = '5400250000'` | `qc_bh='5400' AND kh_bh='250000'` | 拆解為多列等值,利用組合索引 |
| 分類 | 常用 HINT | 場景 |
|---|---|---|
| 目標模式 | ALL_ROWS / FIRST_ROWS(n) / CHOOSE | 全量報表 vs 分頁查詢首屏 |
| 連接方法 | USE_NL / USE_HASH / USE_MERGE | 小驅動大 NL、大大表 Hash、排序合併 Merge |
| 索引指定 | INDEX(t idx_name) / NO_INDEX(t idx_name) | CBO 選錯索引時強制/禁止 |
| 並行執行 | PARALLEL(t 4) / NO_PARALLEL | 大表全掃描並行化 |
| 動態採樣 | DYNAMIC_SAMPLING(t 4) | 統計缺失臨時補齊 |