FIRST_ROWS (無參數) 與 FIRST_ROWS(n) (9i 引入) 雖同屬「優先回應前幾列」導向,但內部優化邏輯截然不同。本文透過實測案例揭示兩者在執行計畫選擇上的關鍵差異。
| Hint | 優化基礎 | 行為特徵 |
|---|---|---|
FIRST_ROWS | **混合模式**:代價基礎 + 大量規則啟發式 | 傾向使用索引、Nested Loops,甚至忽略統計資訊選擇 Domain Index 等特殊索引 |
FIRST_ROWS(n) | **純代價基礎 (CBO)** | 依統計資訊計算前 n 列成本,選擇真正代價最低路徑 (可能為全表掃描 + Hash Join) |
CREATE TABLE t (id NUMBER PRIMARY KEY, name VARCHAR2(30), docs VARCHAR2(4000));
INSERT INTO t SELECT rownum, object_name, owner||' '||object_type||' '||object_name FROM dba_objects;
CREATE INDEX ind_t_name ON t(name);
CREATE INDEX ind_t_docs ON t(docs) INDEXTYPE IS CTXSYS.CONTEXT; -- 全文索引
EXEC DBMS_STATS.GATHER_TABLE_STATS(USER, 'T', METHOD_OPT=>'FOR ALL INDEXED COLUMNS SIZE 100');
EXEC DBMS_STATS.GATHER_INDEX_STATS(USER, 'IND_T_DOCS');
SELECT * FROM t
WHERE CONTAINS(docs, 'SYS', 1) > 0
AND name = 'DUAL'
ORDER BY SCORE(1);
-- 無 Hint (CHOOSE 模式,有統計資訊走 CBO)
-- 選擇:INDEX RANGE SCAN (IND_T_NAME) → TABLE ACCESS BY ROWID → SORT ORDER BY
-- Cost = 5
-- FIRST_ROWS Hint
SELECT /*+ FIRST_ROWS */ * FROM t ...
-- 選擇:DOMAIN INDEX (IND_T_DOCS) → TABLE ACCESS BY ROWID
-- Cost = 2643 <-- 規則啟發式誤判全文索引優先,實際極慢
-- FIRST_ROWS(1) Hint
SELECT /*+ FIRST_ROWS(1) */ * FROM t ...
-- 選擇:INDEX RANGE SCAN (IND_T_NAME) → TABLE ACCESS BY ROWID → SORT ORDER BY
-- Cost = 5 <-- 代價基礎正確評估,同預設 CBO
FIRST_ROWS 保留舊版 RBO 邏輯:發現 CONTAINS 條件 → 規則認定「Domain Index 適合全文」→ 強制使用,忽略 name='DUAL' 高選擇性 B-tree 索引。FIRST_ROWS(n) 完全依賴統計資訊:計算 name='DUAL' 過濾後僅 1 列,再做全文評分排序成本極低 → 選擇 B-tree 索引。OPTIMIZER_MODE=ALL_ROWS 而非 FIRST_ROWS 的原因:避免規則啟發式導致不可預測的劣質計畫。FIRST_ROWS(n)** (n 為預期顯示列數,如 10、20、100),拒絕無參數 FIRST_ROWS。/*+ FIRST_ROWS(100) */ 配合分頁範本 (見 上一篇) 效果最佳。ALL_ROWS 或 /*+ ALL_ROWS */ 追求總吞吐量。