参考书籍:<<基于Oracle的SQL优化>> 作者:崔华
Hint实际上是一种特殊的注释,它一种固定的格式和位置出现在SQL语句的SQL文本中,它可以影响优化器对执行计划的选择,但这种选择并不是强制的,优化器在某些情况下会忽略目标SQL中的Hint,即使这个Hint的语法和语义都是有效的。
下面列举几种Oracle优化器会忽略目标SQL中的Hint的情况:
1、受关键字影响
查询几个关键字来进行测试,THIS不不是关键字
SCOTT@TNS_PDB01>select keyword,length from v$reserved_words where keyword in ('THIS',',','IS','COMMENT');
KEYWORD LENGTH
---------- ----------
COMMENT 7
, 1
IS 2
SCOTT@TNS_PDB01>desc dept;
Name Null? Type
----------------------------------------------------------- -------- -----------------------------------------
DEPTNO NOT NULL NUMBER(2)
DNAME VARCHAR2(14)
LOC VARCHAR2(13)
SCOTT@TNS_PDB01>select * from dept;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SCOTT@TNS_PDB01>set autotrace traceonly;
SCOTT@TNS_PDB01>select deptno from dept;
Execution Plan
----------------------------------------------------------
Plan hash value: 2913917002
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 4 | 12 | 1 (0)| 00:00:01 |
| 1 | INDEX FULL SCAN | PK_DEPT | 4 | 12 | 1 (0)| 00:00:01 |
----------------------------------------------------------------------------
Statistics
----------------------------------------------------------
42 recursive calls
0 db block gets
6 consistent gets
0 physical reads
0 redo size
618 bytes sent via SQL*Net to client
385 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
4 rows processed
**使用Hint full(dept)让表dept走全表扫描**
SCOTT@TNS_PDB01>select /*+ full(dept) */deptno from dept;
Execution Plan
----------------------------------------------------------
Plan hash value: 3383998547
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 4 | 12 | 3 (0)| 00:00:01 |
| 1 | TABLE ACCESS FULL| DEPT | 4 | 12 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
7 consistent gets
0 physical reads
0 redo size
618 bytes sent via SQL*Net to client
402 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
4 rows processed
在Hint前加上关键字IS后full(dept) 失效了
SCOTT@TNS_PDB01>select /*+ is full(dept) */deptno from dept;
Execution Plan
----------------------------------------------------------
Plan hash value: 2913917002
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 4 | 12 | 1 (0)| 00:00:01 |
| 1 | INDEX FULL SCAN | PK_DEPT | 4 | 12 | 1 (0)| 00:00:01 |
----------------------------------------------------------------------------
Statistics
----------------------------------------------------------
190 recursive calls
0 db block gets
326 consistent gets
0 physical reads
0 redo size
618 bytes sent via SQL*Net to client
405 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
47 sorts (memory)
0 sorts (disk)
4 rows processed
Hint前加上非关键字Hint生效
SCOTT@TNS_PDB01>select /*+ THIS full(dept) */deptno from dept;
Execution Plan
----------------------------------------------------------
Plan hash value: 3383998547
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 4 | 12 | 3 (0)| 00:00:01 |
| 1 | TABLE ACCESS FULL| DEPT | 4 | 12 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------
Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 1 (E - Syntax error (1))
---------------------------------------------------------------------------
1 - SEL$1
E - THIS
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
7 consistent gets
0 physical reads
0 redo size
618 bytes sent via SQL*Net to client
407 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
4 rows processed
#将关键字放在Hint后 Hint生效
SCOTT@TNS_PDB01>select /*+ full(dept) comment */deptno from dept;
Execution Plan
----------------------------------------------------------
Plan hash value: 3383998547
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 4 | 12 | 3 (0)| 00:00:01 |
| 1 | TABLE ACCESS FULL| DEPT | 4 | 12 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
7 consistent gets
0 physical reads
0 redo size
618 bytes sent via SQL*Net to client
410 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
4 rows processed
可以看出,如果要在Hint中使用注释,最好将注释放在Hint后,这样可以避免注释中如果出现关键字会忽略掉Hint的情况出现。
2、多个Hint之间自相矛盾
SCOTT@TNS_PDB01>select * from dept;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SCOTT@TNS_PDB01>select index_name from dba_indexes where table_name = 'DEPT' and owner = 'SCOTT';
INDEX_NAME
---------------
PK_DEPT
使用Hint让目标SQL走快速全扫描
SCOTT@TNS_PDB01>set autotrace traceonly;
SCOTT@TNS_PDB01>select /*+ index_ffs(d pk_dept) */deptno from dept d;
Execution Plan
----------------------------------------------------------
Plan hash value: 2578398298
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 4 | 12 | 2 (0)| 00:00:01 |
| 1 | INDEX FAST FULL SCAN| PK_DEPT | 4 | 12 | 2 (0)| 00:00:01 |
--------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
107 recursive calls
0 db block gets
113 consistent gets
1 physical reads
0 redo size
618 bytes sent via SQL*Net to client
414 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
8 sorts (memory)
0 sorts (disk)
4 rows processed
加入Hint full(d)这和 index_ffs(d pk_dept)向矛盾所以2个Hint都失效
SCOTT@TNS_PDB01>select /*+ index_ffs(d pk_dept) full(d) */deptno from dept d;
Execution Plan
----------------------------------------------------------
Plan hash value: 2913917002
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 4 | 12 | 1 (0)| 00:00:01 |
| 1 | INDEX FULL SCAN | PK_DEPT | 4 | 12 | 1 (0)| 00:00:01 |
----------------------------------------------------------------------------
Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 2 (U - Unused (2))
---------------------------------------------------------------------------
1 - SEL$1 / D@SEL$1
U - full(d) / hint conflicts with another in sibling query block
U - index_ffs(d pk_dept) / hint conflicts with another in sibling query block
Statistics
----------------------------------------------------------
30 recursive calls
0 db block gets
39 consistent gets
0 physical reads
0 redo size
618 bytes sent via SQL*Net to client
422 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
5 sorts (memory)
0 sorts (disk)
4 rows processed
3、使用的Hint无效
SCOTT@TNS_PDB01>desc emp;
Name Null? Type
----------------------------------------------------------- -------- -----------------------------------------
EMPNO NOT NULL NUMBER(4)
ENAME VARCHAR2(10)
JOB VARCHAR2(9)
MGR NUMBER(4)
HIREDATE DATE
SAL NUMBER(7,2)
COMM NUMBER(7,2)
DEPTNO NUMBER(2)
SCOTT@TNS_PDB01>desc dept;
Name Null? Type
----------------------------------------------------------- -------- -----------------------------------------
DEPTNO NOT NULL NUMBER(2)
DNAME VARCHAR2(14)
LOC VARCHAR2(13)
SCOTT@TNS_PDB01>set autotrace traceonly;
SCOTT@TNS_PDB01>select e.empno,e.ename,d.loc from emp e,dept d where e.deptno = d.deptno;
14 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 844388907
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 14 | 336 | 6 (17)| 00:00:01 |
| 1 | MERGE JOIN | | 14 | 336 | 6 (17)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID| DEPT | 4 | 44 | 2 (0)| 00:00:01 |
| 3 | INDEX FULL SCAN | PK_DEPT | 4 | | 1 (0)| 00:00:01 |
|* 4 | SORT JOIN | | 14 | 182 | 4 (25)| 00:00:01 |
| 5 | TABLE ACCESS FULL | EMP | 14 | 182 | 3 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("E"."DEPTNO"="D"."DEPTNO")
filter("E"."DEPTNO"="D"."DEPTNO")
Statistics
----------------------------------------------------------
43 recursive calls
0 db block gets
78 consistent gets
0 physical reads
0 redo size
975 bytes sent via SQL*Net to client
411 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
5 sorts (memory)
0 sorts (disk)
14 rows processed
想让目标SQL走HASH连接
SCOTT@TNS_PDB01>select /*+ use_hash(d) */e.empno,e.ename,d.loc from emp e,dept d where e.deptno = d.deptno;
14 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 844388907
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 14 | 336 | 6 (17)| 00:00:01 |
| 1 | MERGE JOIN | | 14 | 336 | 6 (17)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID| DEPT | 4 | 44 | 2 (0)| 00:00:01 |
| 3 | INDEX FULL SCAN | PK_DEPT | 4 | | 1 (0)| 00:00:01 |
|* 4 | SORT JOIN | | 14 | 182 | 4 (25)| 00:00:01 |
| 5 | TABLE ACCESS FULL | EMP | 14 | 182 | 3 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("E"."DEPTNO"="D"."DEPTNO")
filter("E"."DEPTNO"="D"."DEPTNO")
Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 1 (U - Unused (1))
---------------------------------------------------------------------------
2 - SEL$1 / D@SEL$1
U - use_hash(d)
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
10 consistent gets
0 physical reads
0 redo size
975 bytes sent via SQL*Net to client
452 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
14 rows processed
可以看出use_hash(d) Hint被忽略为什么? 因为use_hash(被驱动表)而dept为驱动表,解决办法:修改为Hint use_hash(e)
SCOTT@TNS_PDB01>select /*+ use_hash(e) */e.empno,e.ename,d.loc from emp e,dept d where e.deptno = d.deptno;
14 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 615168685
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 14 | 336 | 6 (0)| 00:00:01 |
|* 1 | HASH JOIN | | 14 | 336 | 6 (0)| 00:00:01 |
| 2 | TABLE ACCESS FULL| DEPT | 4 | 44 | 3 (0)| 00:00:01 |
| 3 | TABLE ACCESS FULL| EMP | 14 | 182 | 3 (0)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("E"."DEPTNO"="D"."DEPTNO")
Statistics
----------------------------------------------------------
29 recursive calls
0 db block gets
80 consistent gets
0 physical reads
0 redo size
1036 bytes sent via SQL*Net to client
452 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
4 sorts (memory)
0 sorts (disk)
14 rows processed
或者结合leading来修改目标SQL的驱动表为emp
SCOTT@TNS_PDB01>select /*+ leading(e) use_hash(d) */e.empno,e.ename,d.loc from emp e,dept d where e.deptno = d.deptno;
14 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 1123238657
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 14 | 336 | 6 (0)| 00:00:01 |
|* 1 | HASH JOIN | | 14 | 336 | 6 (0)| 00:00:01 |
| 2 | TABLE ACCESS FULL| EMP | 14 | 182 | 3 (0)| 00:00:01 |
| 3 | TABLE ACCESS FULL| DEPT | 4 | 44 | 3 (0)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("E"."DEPTNO"="D"."DEPTNO")
Statistics
----------------------------------------------------------
5 recursive calls
0 db block gets
13 consistent gets
0 physical reads
0 redo size
975 bytes sent via SQL*Net to client
463 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
14 rows processed
4、受到子查询转换干扰
SCOTT@TNS_PDB01>create table jobs as select empno,job from emp;
Table created.
SCOTT@TNS_PDB01>select * from jobs;
EMPNO JOB
---------- ----------
7369 CLERK
7499 SALESMAN
...
SCOTT@TNS_PDB01>set autotrace traceonly;
SCOTT@TNS_PDB01>select /*+ ordered cardinality(e 100) */ e.ename,j.job,e.sal,v.avg_sal from emp e,jobs j,(select /*+ merge */e.deptno,avg(e.sal) avg_sal from emp e,dept d where d.loc = 'CHICAGO' and d.deptno = e.deptno group by e.deptno) v where e.empno = j.empno and e.deptno = v.deptno and e.sal > v.avg_sal order by e.ename;
Execution Plan
----------------------------------------------------------
Plan hash value: 2080994986
-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 9 | 531 | 13 (16)| 00:00:01 |
|* 1 | FILTER | | | | | |
| 2 | SORT GROUP BY | | 9 | 531 | 13 (16)| 00:00:01 |
|* 3 | HASH JOIN | | 167 | 9853 | 12 (9)| 00:00:01 |
|* 4 | TABLE ACCESS FULL | DEPT | 1 | 11 | 3 (0)| 00:00:01 |
|* 5 | HASH JOIN | | 500 | 24000 | 9 (12)| 00:00:01 |
| 6 | MERGE JOIN | | 100 | 4100 | 6 (17)| 00:00:01 |
| 7 | TABLE ACCESS BY INDEX ROWID| EMP | 100 | 2900 | 2 (0)| 00:00:01 |
| 8 | INDEX FULL SCAN | PK_EMP | 14 | | 1 (0)| 00:00:01 |
|* 9 | SORT JOIN | | 14 | 168 | 4 (25)| 00:00:01 |
| 10 | TABLE ACCESS FULL | JOBS | 14 | 168 | 3 (0)| 00:00:01 |
| 11 | TABLE ACCESS FULL | EMP | 14 | 98 | 3 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("E"."SAL">SUM("E"."SAL")/COUNT("E"."SAL"))
3 - access("D"."DEPTNO"="E"."DEPTNO")
4 - filter("D"."LOC"='CHICAGO')
5 - access("E"."DEPTNO"="E"."DEPTNO")
9 - access("E"."EMPNO"="J"."EMPNO")
filter("E"."EMPNO"="J"."EMPNO")
Statistics
----------------------------------------------------------
143 recursive calls
5 db block gets
312 consistent gets
11 physical reads
924 redo size
898 bytes sent via SQL*Net to client
680 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
26 sorts (memory)
0 sorts (disk)
2 rows processed
分析:ordered Hint会让目标SQL 按照 emp jobs和内联视图v基表的执行顺序执行目标SQL的,但是内联视图和外部做了视图合并忽略掉了ordered Hint。
解决办法:内联视图v使用Hint no_merge不做视图合并
SCOTT@TNS_PDB01>select /*+ ordered cardinality(e 100) */ e.ename,j.job,e.sal,v.avg_sal from emp e,jobs j,(select /*+ no_merge */e.deptno,avg(e.sal) avg_sal from emp e,dept d where d.loc = 'CHICAGO' and d.deptno = e.deptno group by e.deptno) v where e.empno = j.empno and e.deptno = v.deptno and e.sal > v.avg_sal order by e.ename;
Execution Plan
----------------------------------------------------------
Plan hash value: 596742545
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 5 | 225 | 13 (24)| 00:00:01 |
| 1 | SORT ORDER BY | | 5 | 225 | 13 (24)| 00:00:01 |
|* 2 | HASH JOIN | | 5 | 225 | 12 (17)| 00:00:01 |
| 3 | MERGE JOIN | | 100 | 2900 | 6 (17)| 00:00:01 |
| 4 | TABLE ACCESS BY INDEX ROWID | EMP | 100 | 1700 | 2 (0)| 00:00:01 |
| 5 | INDEX FULL SCAN | PK_EMP | 14 | | 1 (0)| 00:00:01 |
|* 6 | SORT JOIN | | 14 | 168 | 4 (25)| 00:00:01 |
| 7 | TABLE ACCESS FULL | JOBS | 14 | 168 | 3 (0)| 00:00:01 |
| 8 | VIEW | | 3 | 48 | 6 (17)| 00:00:01 |
| 9 | HASH GROUP BY | | 3 | 54 | 6 (17)| 00:00:01 |
| 10 | MERGE JOIN | | 5 | 90 | 6 (17)| 00:00:01 |
|* 11 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | 11 | 2 (0)| 00:00:01 |
| 12 | INDEX FULL SCAN | PK_DEPT | 4 | | 1 (0)| 00:00:01 |
|* 13 | SORT JOIN | | 14 | 98 | 4 (25)| 00:00:01 |
| 14 | TABLE ACCESS FULL | EMP | 14 | 98 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("E"."DEPTNO"="V"."DEPTNO")
filter("E"."SAL">"V"."AVG_SAL")
6 - access("E"."EMPNO"="J"."EMPNO")
filter("E"."EMPNO"="J"."EMPNO")
11 - filter("D"."LOC"='CHICAGO')
13 - access("D"."DEPTNO"="E"."DEPTNO")
filter("D"."DEPTNO"="E"."DEPTNO")
Statistics
----------------------------------------------------------
73 recursive calls
0 db block gets
140 consistent gets
1 physical reads
0 redo size
898 bytes sent via SQL*Net to client
683 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
15 sorts (memory)
0 sorts (disk)
2 rows processed
可以看出ordered、cadinality这两个Hint都生效了。