#hint
hint::=
SQL在查询优化阶段,优化器根据内定规则确定执行计划;作为辅助手段, 开发者可以通过在语句中书写hint,指示优化器改变执行路径,以便快速的查询数据。
优化器可以根据系统统计信息动态调整执行计划,使用hint则对执行计划进行了唯一指定,所以应该被谨慎使用,只有在已获取到足够的统计信息,确认改变执行路径是对性能更优的方式,才建议给语句增加hint。当语句涉及的表结构、业务场景或数据量等信息发生变化时,应通过查看执行计划等手段,重新审视该hint是否合适。
hint有时候会失效,如在hint中指定了非法的顺序(OUTER JOIN则不允许指定顺序),或者指定了非法的JOIN类型等。正常情况下,优化器会优先选择有效的hint,即使该hint的代价更高,但是对于前述非法的hint,优化器会不选择该hint,并在执行计划中输出hint unused。
hint_name
hint提示项的名称,可以同时指定多项hint_name作为组合提示。
如下标识符将被作为YashanDB认可的hint提示项名称:
NO_USE_HASH NO_USE_MERGE NO_USE_NL USE_HASH USE_MERGE USE_NL
hint_intersperse
对于某些提示,需要提供更多的信息来结合指定,例如指定使用索引提示时的索引名称。
YashanDB中使用hint的规则如下:
只有跟在SELECT、UPDATE、INSERT、MERGE或DELETE等关键字后面,且以”/*+ ” 作为开始( “+“ 号后面需有空格 ),“*/” 作为结束的语句块 ,才会被解析成hint。
一个关键字后跟多组”/*+ */”时,只有第一组被解析成hint,其他忽略。
一个hint里某项提示出现如下情况时,优化器忽略该项提示:
- 拼写错误或语法错误
- 不能识别的提示项名称
- 与其他提示项相互冲突
- hint_intersperse里包含了查询块
- 在SQL语句中为表对象定义了别名,而提示中未使用别名
示例
--创建视图
CREATE OR REPLACE VIEW b AS
SELECT * FROM branches WHERE branch_no = '0101';
--hint_intersperse里包含了查询块,该项提示被忽略
EXPLAIN SELECT /*+ FULL(b) */ *
FROM area, b
WHERE area.area_no = '04'
AND area.area_no = b.area_no;
PLAN_DESCRIPTION
----------------------------------------------------------------
SQL hash value: 3312245078
Optimizer: ADOPT_C
+----+--------------------------------+----------------------+------------+----------+-------------+--------------------------------+
| Id | Operation type | Name | Owner | Rows | Cost(%CPU) | Partition info |
+----+--------------------------------+----------------------+------------+----------+-------------+--------------------------------+
| 0 | SELECT STATEMENT | | | | | |
|* 1 | MERGE JOIN | | | 102| 296( 0)| |
| 2 | MERGE SORT | | | | | |
| 3 | TABLE ACCESS BY INDEX ROWID | AREA | SALES | | | |
|* 4 | INDEX UNIQUE SCAN | SYS_C_18 | SALES | 1| 148( 0)| |
| 5 | MERGE SORT | | | | | |
|* 6 | TABLE ACCESS BY INDEX ROWID | BRANCHES | SALES | | | |
|* 7 | INDEX UNIQUE SCAN | SYS_C_20 | SALES | 1| 148( 0)| |
+----+--------------------------------+----------------------+------------+----------+-------------+--------------------------------+
Operation Information (identified by operation id):
---------------------------------------------------
1 - Predicate : access( "AREA"."AREA_NO" = "BRANCHES"."AREA_NO" )
filter("AREA"."AREA_NO" = "BRANCHES"."AREA_NO")
4 - Predicate : access("AREA"."AREA_NO" = '04')
6 - Predicate : filter('04' = "BRANCHES"."AREA_NO")
7 - Predicate : access("BRANCHES"."BRANCH_NO" = '0101')
Hint Information :
---------------------------------------------------
FULL(b) / unresolved
--在branches表上新增一个索引
CREATE INDEX idx_branches_1 ON branches(area_no);
--提示项冲突
EXPLAIN SELECT /*+ FULL(b) INDEX(b IDX_BRANCHES_1) */ *
FROM branches b
WHERE b.branch_no='0401' AND b.area_no='04';
PLAN_DESCRIPTION
----------------------------------------------------------------
SQL hash value: 3081809351
Optimizer: ADOPT_C
+----+--------------------------------+----------------------+------------+----------+-------------+--------------------------------+
| Id | Operation type | Name | Owner | Rows | Cost(%CPU) | Partition info |
+----+--------------------------------+----------------------+------------+----------+-------------+--------------------------------+
| 0 | SELECT STATEMENT | | | | | |
|* 1 | TABLE ACCESS BY INDEX ROWID | BRANCHES | SALES | | | |
|* 2 | INDEX UNIQUE SCAN | SYS_C_20 | SALES | 1| 148( 0)| |
+----+--------------------------------+----------------------+------------+----------+-------------+--------------------------------+
Operation Information (identified by operation id):
---------------------------------------------------
1 - Predicate : filter("B"."AREA_NO" = '04')
2 - Predicate : access("B"."BRANCH_NO" = '0401')
Hint Information :
---------------------------------------------------
FULL(b) / conflict with another
INDEX(b idx_branches_1) / conflict with another
# full提示项
使用FULL提示项时,需同时使用hint_intersperse指定表名,表示对该表执行全表扫描。
示例
SELECT /*+ FULL(a) */ * FROM area a WHERE a.area_no='02';
# index提示项
使用INDEX提示项时,需同时使用hint_intersperse指定表名 [索引名],表示对该表执行指定的索引扫描。
当hint_intersperse中包含多个索引时,优化器从中选择代价最低的索引;当hint_intersperse中未指定任何索引时,优化器从该表上所有的索引中选择代价最低的索引。
示例
--在branches表上新增一个索引
CREATE INDEX idx_branches_2 ON branches(area_no,branch_no);
--提示多个索引,或者不指定索引,优化器将选择代价最低的索引执行扫描
SELECT /*+ INDEX(b) */ *
FROM branches b
WHERE b.branch_no LIKE '0%';
# index_ffs提示项
使用INDEX_FFS提示项时,需同时使用hint_intersperse指定表名 索引名,表示对该表执行指定的索引快速全表扫描。
只有当所有查询列都位于指定的索引列之中,即通过扫描索引就可以得到所有的查询列而不用回表时,优化器才会接受提示选择此索引。
示例
--按上面示例,branches上已存在idx_branches_2索引
SELECT /*+ INDEX_FFS(b idx_branches_2) */ b.area_no,b.branch_no
FROM branches b
WHERE b.area_no='04' AND b.branch_no='0401';
# no_index提示项
使用NO_INDEX提示项时,需同时使用hint_intersperse指定表名 [索引名],表示不使用指定的索引对该表执行索引扫描。
当hint_intersperse中包含多个索引时,这些索引都将不会被优化器使用,但如果该表上存在其他索引,则优化器可能会选择那些索引(由优化器规则决定);当hint_intersperse中未指定任何索引时,则优化器将不会选择该表上的任何索引。
示例
SELECT /*+ NO_INDEX(b) */ *
FROM branches b
WHERE b.branch_no LIKE '0%';
# parallel提示项
使用PARALLEL提示项,可以实现并行查询。为了语法兼容,配置PARALLEL提示项时需使用hint_intersperse同时指定表名和并行度[表名,并行度],但仍表示是对查询操作整体的并行。
多张表同时通过hint指定不同的并行度时,每张表的并行度将全采用此次最大的指定值。
通过hint所指定的并行度最大为255,超过255时会退化为255。
无法跨SELECT指定并行度,此种场景请使用数据库参数DEGREE_OF_PARALLEL来指定。
对并行查询的指定只作为一个参考项,如果优化器评估后认为并行查询不是最优计划,则不会选择生成并行计划。
示例
SELECT /*+ PARALLEL(t1,4)*/ c1 FROM t1;
-- 通过hint对表t1和表t2指定不同的并行度(3、4)时,两个表的并行度将全为4
SELECT /*+ PARALLEL(t1,3) PARALLEL(t2,4)*/ t1.c1,t2.c2 FROM t1,t2;
-- 无效,无法跨SELECT指定并行度
SELECT /*+ PARALLEL(t2,4)*/ c1 FROM t1
UNION
SELECT c2 FROM t2;
# leading提示项
使用LEADING提示项时,需同时使用hint_intersperse指定表名 [表名],表示在多表关联的查询中,提示优化器按照指定顺序访问表。
LEADING提示的表访问顺序为建议顺序,非强制,当遇到无法调整的JOIN类型(OUTER)时,则不会改变顺序。
当hint_intersperse中指定表数量超过参与JOIN的实际表数量时,优化器不接受此项提示。
当hint_intersperse中出现无效的表时,优化器不接受在该表之后指定的顺序,但仍接受该表之前所指定的顺序。
示例
SELECT /*+ LEADING(b e)*/ *
FROM branches b, department d, employees e
WHERE b.branch_no = e.branch
AND d.deparment_no = e.department
AND b.branch_no = '0401'
AND d.deparment_no = '000';
# no_use_hash提示项
使用NO_USE_HASH提示项时,需同时使用hint_intersperse指定表名 [表名],表示在指定的这些表与其他表进行关联查询时,提示优化器不进行HASH JOIN 。
示例
SELECT /*+ NO_USE_HASH(d) */ *
FROM branches b, department d, employees e
WHERE b.branch_no = e.branch
AND d.deparment_no = e.department;
# no_use_merge提示项
使用NO_USE_MERGE提示项时,需同时使用hint_intersperse指定表名 [表名],表示在指定的这些表与其他表进行关联查询时,提示优化器不进行MERGE JOIN。
示例
SELECT /*+ NO_USE_MERGE(d e) */ *
FROM branches b, department d, employees e
WHERE b.branch_no = e.branch
AND d.deparment_no = e.department;
# no_use_nl提示项
使用NO_USE_NL提示项时,需同时使用hint_intersperse指定表名 [表名],表示在指定的这些表与其他表进行关联查询时,提示优化器不进行NEST LOOPS。
示例
SELECT /*+ NO_USE_NL(a) */ *
FROM area a, branches b
WHERE a.area_no = b.area_no
AND a.area_no='04'
AND b.branch_no='0401';
# use_hash提示项
使用USE_HASH提示项时,需同时使用hint_intersperse指定表名 [表名],表示在指定的这些表与其他表进行关联查询时,提示优化器进行HASH JOIN 。
示例
SELECT /*+ USE_HASH(d) */ *
FROM branches b, department d, employees e
WHERE b.branch_no = e.branch
AND d.deparment_no = e.department;
# use_merge提示项
使用USE_MERGE提示项时,需同时使用hint_intersperse指定表名 [表名],表示在指定的这些表与其他表进行关联查询时,提示优化器进行MERGE JOIN。
示例
SELECT /*+ USE_MERGE(d e) */ *
FROM branches b, department d, employees e
WHERE b.branch_no = e.branch
AND d.deparment_no = e.department;
# use_nl提示项
使用USE_NL提示项时,需同时使用hint_intersperse指定表名 [表名],表示在指定的这些表与其他表进行关联查询时,提示优化器进行NEST LOOPS。
示例
SELECT /*+ USE_NL(a) */ *
FROM area a, branches b
WHERE a.area_no = b.area_no
AND a.area_no='04'
AND b.branch_no='0401';
# selectivity提示项
使用SELECTIVITY提示项时,语法与其他hint不同,SELECTIVITY提示项只可跟在filter后,表示指定这些filter的选择率,其后需跟一个0到1之间的浮点数。
AND、OR和BETWEEN的选择率无法通过hint的方式指定,只能通过计算得到。
示例
SELECT c11
FROM hint_tb1
WHERE c11 > 15 SELECTIVITY 0.8;