前置测试表
sql
-- 建测试表+索引
CREATE TABLE t1(id INT PRIMARY KEY,c1 VARCHAR(20));
CREATE INDEX idx_t1_c1 ON t1(c1);
INSERT INTO t1 VALUES(1,'a'),(2,'b'),(3,'c');
COMMIT;
场景 1:业务 SQL 无 hint → 不使用 TO 子句(推荐最简写法)
1. 创建 Outline
sql
-- 带hint的语句作为基准,不写TO
CREATE OR REPLACE OUTLINE ol_no_to
ON SELECT /*+ INDEX(t1 idx_t1_c1) */ * FROM t1 WHERE c1=?;
2. 执行业务原生 SQL(不带 hint)
sql
SELECT * FROM t1 WHERE c1='a';
生效逻辑
OB 会把 ON 后的语句剔除 hint,得到匹配模板 SELECT * FROM t1 WHERE c1=?,参数化后和业务 SQL 完全匹配,自动追加索引 hint,走索引扫描。
验证是否命中 Outline
sql
EXPLAIN EXTENDED SELECT * FROM t1 WHERE c1='a';
-- 执行计划末尾会显示 Outline: ol_no_to
失效场景
如果业务 SQL 自带 hint,如下语句不会命中上面的 ol_no_to
sql
SELECT /*+ FULL(t1) */ * FROM t1 WHERE c1='a';
场景 2:业务 SQL 自带 hint → 必须使用 TO 子句覆盖原有 hint
1. 创建带 TO 的 Outline
sql
CREATE OR REPLACE OUTLINE ol_use_to
-- 左边:我们期望生效的带正确hint的SQL
ON SELECT /*+ INDEX(t1 idx_t1_c1) */ * FROM t1 WHERE c1=?
-- TO 后:线上真实执行、自带错误hint的原始SQL模板
TO SELECT /*+ FULL(t1) */ * FROM t1 WHERE c1=?;
2. 执行带原生 hint 的业务 SQL
sql
SELECT /*+ FULL(t1) */ * FROM t1 WHERE c1='b';
生效逻辑
匹配模板取自 TO 后的语句,无视业务 SQL 自带的 FULL(t1) 全表扫描 hint,强制替换为 INDEX 索引 hint。
验证命中
sql
EXPLAIN EXTENDED SELECT /*+ FULL(t1) */ * FROM t1 WHERE c1='b';
-- 计划中Outline: ol_use_to,执行计划走索引而非全表扫描
配套常用管理 SQL
- 查询已创建的 Outline
sql
SELECT outline_name,sql_text,target_sql_text FROM DBA_OUTLINES;
-
sql_text:ON 后带 hint 的语句 -
target_sql_text:TO 后的匹配语句,无 TO 时该字段为 NULL
- 删除 Outline
sql
DROP OUTLINE IF EXISTS ol_no_to;
DROP OUTLINE IF EXISTS ol_use_to;
不传 TO <target_stmt> (默认模式)
-
匹配逻辑 :系统会将你传入的
stmt_with_hint(带 Hint 的语句)进行参数化 (即把常量替换为?),然后与数据库接收到的所有 SQL 进行参数化文本比对。 -
适用场景 :适用于原始 SQL 本身不含 Hint ,你想通过 Outline 给它“注入”一个执行计划。
-
示例 :
sql
编辑
1CREATE OUTLINE ol1 ON SELECT /*+ INDEX(t1 idx1) */ * FROM t1 WHERE c1 = ?;
此时,所有形如 SELECT * FROM t1 WHERE c1 = 100 的 SQL,只要参数化后与 SELECT * FROM t1 WHERE c1 = ? 一致,就会命中这个 Outline。
传入 TO <target_stmt>
-
匹配逻辑 :系统会将
stmt_with_hint与target_stmt都进行参数化,只有当两者参数化后完全一致 时,才会将stmt_with_hint中的 Hint 应用于target_stmt。 -
适用场景 :适用于原始 SQL 本身已含 Hint ,但你希望用另一个 Hint 覆盖它,或者你想精确控制哪个 SQL 被绑定。
-
关键要求 :
stmt_with_hint和target_stmt在去掉 Hint 后,必须完全匹配 (包括表名、列名、结构等)。 -
示例 :
sql
编辑
1CREATE OUTLINE ol2 ON SELECT /*+ FULL(t1) */ * FROM t1 WHERE c1 = ? TO SELECT /*+ INDEX(t1 idx1) */ * FROM t1 WHERE c1 = ?;
此时,只有当数据库收到的 SQL 是 SELECT /*+ INDEX(t1 idx1) */ * FROM t1 WHERE c1 = 100 时,才会被替换为 FULL(t1) 的执行计划。