Optimizing Oracle Optimizer (2011년)
Oracle은 Hint를 무시하지 않는다 0 0 3,167

by 구루비스터디 Hint [2018.07.14]


오라클이 힌트를 무시하는 것 처럼 보일 때


힌트를 사용할 수 없는 환경일 때



drop table t1 purge;

create table t1(c1 int, c2 varchar2(10), c3 varchar2(10) );

create index t1_n1 on t1(c1);


insert into t1
select level, 'A','a'
  from dual
connect by level <= 10000;


select /*+ index_ffs(t1 t1(c1) */ c1
  from t1;


--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      | 10000 |   126K|     7   (0)| 00:00:01 |
|   1 |  TABLE ACCESS FULL| T1   | 10000 |   126K|     7   (0)| 00:00:01 |
--------------------------------------------------------------------------
=> 힌트를 타지 않았다.(null은 인덱스에 존재하지 않기 때문에)

select /*+ index_ffs(t1 t1(c1) */ c1
  from t1
 where c1 is not null  ;

------------------------------------------------------------------------------
| Id  | Operation            | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |       | 10000 |   126K|     7   (0)| 00:00:01 |
|*  1 |  INDEX FAST FULL SCAN| T1_N1 | 10000 |   126K|     7   (0)| 00:00:01 |
------------------------------------------------------------------------------
=> not null조건 추가로 인해 null인 데이터를 볼 필요가 없으므로 힌트를 탔다



ALTER TABLE t1 MODIFY (c1 int NOT NULL); 

select /*+ index_ffs(t1 t1(c1) */ c1
  from t1
;

------------------------------------------------------------------------------
| Id  | Operation            | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |       | 10000 |   126K|     7   (0)| 00:00:01 |
|   1 |  INDEX FAST FULL SCAN| T1_N1 | 10000 |   126K|     7   (0)| 00:00:01 |
------------------------------------------------------------------------------
=> c1컬럼에 not null 제약조건 추가로 인해 null인 데이터가 없는 것이 확실하므로 인덱스를 탔다.



sub query unnesting 이 발생할 때 ordered 힌트를 줄 때

drop table t2 purge;

create table t2(c1 int, c2 int );

create index t1_n2 on t1(c2);
create index t2_n1 on t2(c1);

insert into t2
select level, level
  from dual
connect by level <= 10000;

create table t3(c1 varchar2(10),c2 varchar2(10) );

insert into t3 values ('A','a');
insert into t3 values ('B','b');
commit;


=> ORDERED 힌트를 이용해서 t1->t2로 조인순서를 변경하고자 할 때
select /*+ ordered */ *
  from t1,t2
 where t1.c2 in (select c1 from t3 where c2 > 'A')
   and t1.c1 = t2.c1;

-----------------------------------------------------------------------------
| Id  | Operation            | Name | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |      | 10000 |   654K|    20  (15)| 00:00:01 |
|*  1 |  HASH JOIN           |      | 10000 |   654K|    20  (15)| 00:00:01 |
|   2 |   TABLE ACCESS FULL  | T2   | 10000 |   253K|     7   (0)| 00:00:01 |
|*  3 |   HASH JOIN          |      | 10000 |   400K|    12  (17)| 00:00:01 |
|   4 |    SORT UNIQUE       |      |     2 |    28 |     3   (0)| 00:00:01 |
|*  5 |     TABLE ACCESS FULL| T3   |     2 |    28 |     3   (0)| 00:00:01 |
|   6 |    TABLE ACCESS FULL | T1   | 10000 |   263K|     7   (0)| 00:00:01 |
----------------------------------------------------------------------------- 
=> t3->t1->t2 순으로 실행되었다.(Subquery Unnesting 발생 함)

select /*+ ordered */ *
  from t1,t2
 where t1.c2 in (select /*+ no_unnest */ c1 from t3 where c2 > 'A')
   and t1.c1 = t2.c1;

----------------------------------------------------------------------------
| Id  | Operation           | Name | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |      |    32 |  1696 |   954   (1)| 00:00:05 |
|*  1 |  FILTER             |      |       |       |            |          |
|*  2 |   HASH JOIN         |      | 10000 |   517K|    15   (7)| 00:00:01 |
|   3 |    TABLE ACCESS FULL| T1   | 10000 |   263K|     7   (0)| 00:00:01 |
|   4 |    TABLE ACCESS FULL| T2   | 10000 |   253K|     7   (0)| 00:00:01 |
|*  5 |   TABLE ACCESS FULL | T3   |     1 |    14 |     3   (0)| 00:00:01 |
----------------------------------------------------------------------------
=> 제대로 힌트가 적용됨


Non Unique Primary Key가 존재하는 경우의 Direct Path Insert
  • 기존의 Non Unique Index에 PK constraint만 추가 할 경우
  • 복잡한 Data처리를 위해 Deferrable Constraint를 사용하는 경우

 
alter table t1
add constraint pk_t1 primary key (c1)
using index
;

insert /*+ append */ into t1
select level+10000000, 'A','a'
  from dual
connect by level <= 10000;

-----------------------------------------------------------------------------
| Id  | Operation                    | Name | Rows  | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------
|   0 | INSERT STATEMENT             |      |     1 |     2   (0)| 00:00:01 |
|*  1 |  CONNECT BY WITHOUT FILTERING|      |       |            |          |
|   2 |   FAST DUAL                  |      |     1 |     2   (0)| 00:00:01 |
-----------------------------------------------------------------------------
=> 오라클은 Non Unique Primary Key가 존재하는 경우에는 Direct Path Insert를 사용하지 못한다.


alter table t1 drop constraint pk_t1;
drop index t1_n1;
alter table t1 add constraint pk_t1 primary key(c1);

insert /*+ append */ into t1
select level, 'A','a'
  from dual
connect by level <= 10000;


"데이터베이스 스터디모임" 에서 2009년에 "OPTIMIZING ORACLE OPTIMIZER " 도서를 스터디하면서 정리한 내용 입니다.

- 강좌 URL : http://www.gurubee.net/lecture/3971

- 구루비 강좌는 개인의 학습용으로만 사용 할 수 있으며, 다른 웹 페이지에 게재할 경우에는 출처를 꼭 밝혀 주시면 고맙겠습니다.~^^

- 구루비 강좌는 서비스 제공을 위한 목적이나, 학원 홍보, 수익을 얻기 위한 용도로 사용 할 수 없습니다.

댓글등록
SQL문을 포맷에 맞게(깔끔하게) 등록하려면 code() 버튼을 클릭하여 작성 하시면 됩니다.
로그인 사용자만 댓글을 작성 할 수 있습니다. 로그인, 회원가입