# 요약
  ■ PGA
■ 자동 PGA 메모리 관리
■ sort operation
■ 조인 방법
    1. nested loop join
    2. sort merge join
    3. hash join 
■ table prefetch (9iR2)
■ batch I/O (11g)


■ PGA
SQL 수행 도중 데이터 정렬이 필요할 때 오라클은 PGA 메모리 sort area를 할당하며 완료여부에 따라 두가지 유형으로 수행된다.
- 메모리 소트 (in memory sort) : 전체 데이터의 정렬 작업을 메모리 내에서 완료,
                                internal sort
- 디스크 소트 (to dist sort) : 할당받은 sort area 내에서 정렬을 완료하지 못해 디스크공간까지 사용, 
                              external sort

sort area 내에서 데이터 정렬을 마무리하는 것이 최적이다.(optimal), 양이 많을 때는 정렬된 중간 결과집합(sort run)을 
temporary tablespace의 temp segment에 임시 저장한다.

- optimal sort   : sort 작업이 PGA의 sort_area_size 안에서 이루어지는 것
- onepass sort   : sort 대상 집합(sort run)이 디스크에 한번만 쓰여지는 것
- multipass sort : sort 대상 집합(sort run)이 디스크에 여러번 쓰여짐으로써 디스크 I/O가 많이 발생하여 성능이 저하된다.





■ 자동 PGA 메모리 관리
- pga_aggregate_target 파라미터를 기반으로 작업영역에 할당되는 PGA 메모리 양이 동적으로 조정된다.
- 기본값 : 10MB 또는 SGA 크기의 20% 중에 더 큰 값으로 설정
    alter session set workarea_size_policy = auto;
    
- 수동관리 (8i)
    alter session set workarea_size_policy = manual;
    
    alter session set sort_area_size = 1M;
    alter session set hash_area_size = 1M;
    alter session set create_bitmap_area_size = 1M;
    alter session set bitmap_merge_area_size = 1M;

-- server process > pga > sql 작업영역 = workarea_size_policy


# 히든 파라미터 포함 정보
SYS@ora11g> select a.ksppinm name, b.ksppstvl value
            from x$ksppi a, x$ksppsv b
            where a.indx = b.indx
            and lower(a.ksppinm) in ('workarea_size_policy','sort_area_size','_smm_max_size');

NAME                                          VALUE
--------------------------------------------- --------------------------------------------------
sort_area_size                                1048576   byte (1MB)
workarea_size_policy                          AUTO
_smm_max_size                                 48332     Kbyte  (50MB)

# 각 서버 프로세스가 사용가능한 work area 최대 크기는 _smm_max_size 파리미터로 확인(KB단위)


HR@ora11g> create table emp_temp 
           as select * from employees, 
                            (select rownum no from dual connect by level <= 1000);

Table created.


HR@ora11g> select count(*) from emp_temp;

  COUNT(*)
----------
    107000


HR@ora11g> select /*+ gather_plan_statistics */ *
           from (select rownum as id, employee_id from emp_temp order by employee_id)
           where id = 1;

        ID EMPLOYEE_ID
---------- -----------
         1         198


HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------
SQL_ID  4yy8ff8yp198d, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ * from (select rownum as id,
employee_id from emp_temp order by employee_id) where id = 1

Plan hash value: 1963048918

--------------------------------------------------------------------------------------------------------------
| Id | Operation            | Name    |Starts|E-Rows|A-Rows|   A-Time  |Buffers|Reads| OMem | 1Mem| Used-Mem |
--------------------------------------------------------------------------------------------------------------
|   0| SELECT STATEMENT     |         |     1|      |    1 |00:00:00.02|   1148| 1145|      |     |          |
|*  1|  VIEW                |         |     1| 92220|    1 |00:00:00.02|   1148| 1145|      |     |          |
|   2|   SORT ORDER BY      |         |     1| 92220|  107K|00:00:00.03|   1148| 1145| 2887K| 760K| 2566K (0)|
|   3|    COUNT             |         |     1|      |  107K|00:00:00.02|   1148| 1145|      |     |          |
|   4|     TABLE ACCESS FULL| EMP_TEMP|     1| 92220|  107K|00:00:00.01|   1148| 1145|      |     |          |
--------------------------------------------------------------------------------------------------------------


Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("ID"=1)

Note
-----
   - dynamic sampling used for this statement (level=2)


- E-Rows    : 각 operation이 끝났을 때 return되는 건수(예상치)
- A-Rows    : 각 operation이 끝났을 때 return되는 건수(실제)
- A-Time    : 실제 실행 시간
- Buffers   : 각 operation이 disk에 메모리에서 읽은 block 수
- Reads     : 각 operation이 disk에 읽은 block 수
- Writes    : 각 operation이 disk에 write한 block 수
- OMem      : optimal sort에 필요한 예상 정렬 작업영역의 크기
- 1Mem      : one pass sort에 필요한 예상 정렬 작업영역의 크기
- Used-Mem  : 실제로 사용된 정렬 작업 영역의 크기
       (0)  : optimal sort
       (1)  : onepass sort
       (2~) : muntipass sort
- Used-Temp : 메모리가 부족하여 temporary space가 사용된 공간 크기(kb단위)




# workarea_size_policy = manual
HR@ora11g> alter session set workarea_size_policy = manual;

Session altered.


HR@ora11g> alter session set sort_area_size = 1048576;

Session altered.


HR@ora11g> select /*+ gather_plan_statistics */ *
           from (select rownum as id, employee_id from emp_temp order by employee_id)
           where id = 1;

        ID EMPLOYEE_ID
---------- -----------
         1         198


HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------
SQL_ID  4yy8ff8yp198d, child number 1
-------------------------------------
select /*+ gather_plan_statistics */ * from (select rownum as id,
employee_id from emp_temp order by employee_id) where id = 1

Plan hash value: 1963048918

--------------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation            | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  | Writes | OMem |  1Mem | Used-Mem | Used-Tmp|
--------------------------------------------------------------------------------------------------------------------------------------------------
|  0 | SELECT STATEMENT     |          |      1 |        |      1 |00:00:00.18 |    1152 |   1358 |    213 |      |       |          |         |
|* 1 |  VIEW                |          |      1 |  92220 |      1 |00:00:00.18 |    1152 |   1358 |    213 |      |       |          |         |
|  2 |   SORT ORDER BY      |          |      1 |  92220 |    107K|00:00:00.18 |    1152 |   1358 |    213 | 1917K|   660K| 1009K (1)|    2048 |
|  3 |    COUNT             |          |      1 |        |    107K|00:00:00.02 |    1148 |   1145 |      0 |      |       |          |         |
|  4 |     TABLE ACCESS FULL| EMP_TEMP |      1 |  92220 |    107K|00:00:00.01 |    1148 |   1145 |      0 |      |       |          |         |
--------------------------------------------------------------------------------------------------------------------------------------------------


Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("ID"=1)

Note
-----
   - dynamic sampling used for this statement (level=2)


> 메모리를 작게 구성하니 onepass sort 발생, 2048kb 공간 사용





HR@ora11g> alter session set sort_area_size = 0;

Session altered.

>> 0으로 해도 오라클이 최저 크기로 만든다


HR@ora11g> select /*+ gather_plan_statistics */ *
           from (select rownum as id, employee_id from emp_temp order by employee_id)
           where id = 1;

        ID EMPLOYEE_ID
---------- -----------
         1         198

HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------
SQL_ID  4yy8ff8yp198d, child number 2
-------------------------------------
select /*+ gather_plan_statistics */ * from (select rownum as id,
employee_id from emp_temp order by employee_id) where id = 1

Plan hash value: 1963048918

--------------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation            | Name    |Starts|E-Rows|A-Rows |   A-Time  |Buffers|Reads|Writes|OMem |1Mem|Used-Mem |Used-Tmp|
--------------------------------------------------------------------------------------------------------------------------------------------------
|  0 | SELECT STATEMENT     |         |     1|      |     1 |00:00:03.91|   1208| 1679|   534|     |    |         |        |
|* 1 |  VIEW                |         |     1| 92220|     1 |00:00:03.91|   1208| 1679|   534|     |    |         |        |
|  2 |   SORT ORDER BY      |         |     1| 92220|   107K|00:00:03.91|   1208| 1679|   534|2007K|670K|99328 (12|   3072 |
|  3 |    COUNT             |         |     1|      |   107K|00:00:00.02|   1148| 1145|     0|     |    |         |        |
|  4 |     TABLE ACCESS FULL| EMP_TEMP|     1| 92220|   107K|00:00:00.01|   1148| 1145|     0|     |    |         |        |
--------------------------------------------------------------------------------------------------------------------------------------------------


Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("ID"=1)

Note
-----
   - dynamic sampling used for this statement (level=2)


> 0으로 설정해도 99328kb는 사용 됨, 12번 I/O 발생(multipass sort)
> temp 공간을 사용함으로써 Writes 발생



SYS@ora11g> select sql_id, child_number from v$sql
            where sql_text like '%gather_plan_statistics%' and sql_text not like '%v$sql$';

SQL_ID        CHILD_NUMBER
------------- ------------
4yy8ff8yp198d            0
4yy8ff8yp198d            1
4yy8ff8yp198d            2


# sql workarea 사용량
SYS@ora11g> select child_number, last_execution, last_memory_used, last_tempseg_size
            from v$sql_workarea where sql_id = '4yy8ff8yp198d';

CHILD_NUMBER LAST_EXECUTION                           LAST_MEMORY_USED LAST_TEMPSEG_SIZE
------------ ---------------------------------------- ---------------- -----------------
           2 12 PASSES                                           99328           3145728
           1 1 PASS                                            1033216           2097152
           0 OPTIMAL                                           2627584





HR@ora11g> alter session set sort_area_size = 0;

Session altered.

HR@ora11g> alter session set tracefile_identifier = 'sort';

Session altered.

HR@ora11g> alter session set events '10046 trace name context forever, level 8';

Session altered.

HR@ora11g> select *
           from (select rownum as id, employee_id from emp_temp order by employee_id)
           where id = 1;

        ID EMPLOYEE_ID
---------- -----------
         1         198

HR@ora11g> alter session set events '10046 trace name context off';

Session altered.

HR@ora11g> !
[oracle@oracle diag]$ cd $ORACLE_BASE/diag/rdbms/ora11g/ora11g/trace

[oracle@oracle trace]$ ls *sort.trc
ora11g_ora_21353_sort.trc

[oracle@oracle trace]$ tkprof ora11g_ora_21353_sort.trc sort_21353.txt sys=no

[oracle@oracle trace]$ cat sort_21353.txt
...
select *
           from (select rownum as id, employee_id from emp_temp order by employee_id)
           where id = 1

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          2          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2      0.07       0.84       1679       1148         60           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      0.07       0.84       1679       1150         60           1

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 84
Number of plan statistics captured: 1

Rows (1st) Rows (avg) Rows (max)  Row Source Operation
---------- ---------- ----------  ---------------------------------------------------
         1          1          1  VIEW  (cr=1148 pr=1679 pw=534 time=840050 us cost=1531 size=2397720 card=92220)
    107000     107000     107000   SORT ORDER BY (cr=1148 pr=1679 pw=534 time=843902 us cost=1531 size=1198860 card=92220)
    107000     107000     107000    COUNT  (cr=1148 pr=1145 pw=0 time=15499 us)
    107000     107000     107000     TABLE ACCESS FULL EMP_TEMP (cr=1148 pr=1145 pw=0 time=6411 us cost=320 size=1198860 card=92220)


Elapsed times include waiting on following events:
  Event waited on                             Times   Max. Wait  Total Waited
  ----------------------------------------   Waited  ----------  ------------
  SQL*Net message to client                       2        0.00          0.00
  direct path read                               26        0.00          0.00
  direct path write temp                        534        0.02          0.79
  direct path read temp                         274        0.00          0.00
  SQL*Net message from client                     2        4.55          4.55
********************************************************************************

direct path read : 데이터버퍼캐시 사용안하고 커서로 바로 읽기 25번 I/O 발생
direct path write temp : 템프에 내린 I/O 수, 멀티패스 소트 때문에 발생
direct path read temp : 템프에서 읽은 I/O 수, 멀티패스 소트때문에 발생











■ sort operation

HR@ora11g> select /*+ gather_plan_statistics */ * from employees order by salary desc;

107 rows selected.


HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------
SQL_ID  f02nc674zaj5s, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ * from employees order by salary desc

Plan hash value: 3447538987

---------------------------------------------------------------------------------------------------------------------
| Id  | Operation          | Name      | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
---------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |           |      1 |        |    107 |00:00:00.01 |       6 |       |       |          |
|   1 |  SORT ORDER BY     |           |      1 |    107 |    107 |00:00:00.01 |       6 | 16384 | 16384 |14336  (0)|
|   2 |   TABLE ACCESS FULL| EMPLOYEES |      1 |    107 |    107 |00:00:00.01 |       6 |       |       |          |
---------------------------------------------------------------------------------------------------------------------





HR@ora11g> select /*+ gather_plan_statistics */ department_id, sum(salary) from employees group by department_id;

DEPARTMENT_ID SUM(SALARY)
------------- -----------
          110       20308
          100       51608
           30       24900
                     7000
           50      156400
           40        6500
           80      304500
           20       20900
           70       10000
           90       36000
           10        4400
           60       28800


HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------
SQL_ID  afpw0rwy4hmw2, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ department_id, sum(salary) from employees group by department_id

Plan hash value: 1192169904

---------------------------------------------------------------------------------------------------------------------
| Id  | Operation          | Name      | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
---------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |           |      1 |        |     12 |00:00:00.01 |       6 |       |       |          |
|   1 |  HASH GROUP BY     |           |      1 |     11 |     12 |00:00:00.01 |       6 |  1078K|  1078K|  220K (0)|
|   2 |   TABLE ACCESS FULL| EMPLOYEES |      1 |    107 |    107 |00:00:00.01 |       6 |       |       |          |
---------------------------------------------------------------------------------------------------------------------

> HASH GROUP BY : 나누기를 통해 그룹화, 정렬X





HR@ora11g> select /*+ gather_plan_statistics */ distinct department_id from employees;

DEPARTMENT_ID
-------------
          110
          100
           30

           50
           40
           80
           20
           70
           90
           10
           60


HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------
SQL_ID  8sqgj5ks87pv6, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ distinct department_id from employees

Plan hash value: 3042654289

---------------------------------------------------------------------------------------------------------------------
| Id  | Operation          | Name      | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
---------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |           |      1 |        |     12 |00:00:00.01 |       6 |       |       |          |
|   1 |  HASH UNIQUE       |           |      1 |     11 |     12 |00:00:00.01 |       6 |  1159K|  1159K|  212K (0)|
|   2 |   TABLE ACCESS FULL| EMPLOYEES |      1 |    107 |    107 |00:00:00.01 |       6 |       |       |          |
---------------------------------------------------------------------------------------------------------------------

> HASH UNIQUE : hash 알고리즘, 정렬X





SYS@ora11g> select a.ksppinm name, b.ksppstvl value
            from x$ksppi a, x$ksppsv b
            where a.indx = b.indx
            and lower(a.ksppinm) in ('_gby_hash_aggregation_enabled');

NAME                                          VALUE
--------------------------------------------- --------------------------------------------------
_gby_hash_aggregation_enabled                 TRUE



# 해시가 아닌 소트 그룹바이로 사용하겠다 (세션 레벨)
HR@ora11g> alter session set "_gby_hash_aggregation_enabled" = false;

Session altered.



HR@ora11g> select /*+ gather_plan_statistics */ department_id, sum(salary) from employees group by department_id;

DEPARTMENT_ID SUM(SALARY)
------------- -----------
           10        4400
           20       20900
           30       24900
           40        6500
           50      156400
           60       28800
           70       10000
           80      304500
           90       36000
          100       51608
          110       20308
                     7000

12 rows selected.

HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------
SQL_ID  afpw0rwy4hmw2, child number 1
-------------------------------------
select /*+ gather_plan_statistics */ department_id, sum(salary) from
employees group by department_id

Plan hash value: 2107619104

---------------------------------------------------------------------------------------------------------------------
| Id  | Operation          | Name      | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
---------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |           |      1 |        |     12 |00:00:00.01 |       6 |       |       |          |
|   1 |  SORT GROUP BY     |           |      1 |     11 |     12 |00:00:00.01 |       6 |  3072 |  3072 | 2048  (0)|
|   2 |   TABLE ACCESS FULL| EMPLOYEES |      1 |    107 |    107 |00:00:00.01 |       6 |       |       |          |
---------------------------------------------------------------------------------------------------------------------



HR@ora11g> select /*+ gather_plan_statistics */ distinct department_id from employees;

DEPARTMENT_ID
-------------
           10
           20
           30
           40
           50
           60
           70
           80
           90
          100
          110


12 rows selected.


HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------
SQL_ID  8sqgj5ks87pv6, child number 1
-------------------------------------
select /*+ gather_plan_statistics */ distinct department_id from employees

Plan hash value: 4280862484

---------------------------------------------------------------------------------------------------------------------
| Id  | Operation          | Name      | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
---------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |           |      1 |        |     12 |00:00:00.01 |       6 |       |       |          |
|   1 |  SORT UNIQUE       |           |      1 |     11 |     12 |00:00:00.01 |       6 |  3072 |  3072 | 2048  (0)|
|   2 |   TABLE ACCESS FULL| EMPLOYEES |      1 |    107 |    107 |00:00:00.01 |       6 |       |       |          |
---------------------------------------------------------------------------------------------------------------------






HR@ora11g> alter session set "_gby_hash_aggregation_enabled" = true;

Session altered.





HR@ora11g> select /*+ gather_plan_statistics */ employee_id, last_name from employees where job_id = 'ST_CLERK'
minus
select employee_id, last_name from employees where salary > 10000;

20 rows selected.


HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------
SQL_ID  a4fwd6r159wp2, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ employee_id, last_name from
employees where job_id = 'ST_CLERK' minus select employee_id, last_name
from employees where salary > 10000

Plan hash value: 1571493292

------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name       | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads |  OMem |  1Mem | Used-Mem |
------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |            |      1 |        |     20 |00:00:00.01 |       8 |      1|       |       |          |
|   1 |  MINUS                        |            |      1 |        |     20 |00:00:00.01 |       8 |      1|       |       |          |
|   2 |   SORT UNIQUE                 |            |      1 |      6 |     20 |00:00:00.01 |       2 |      1|  3072 |  3072 | 2048  (0)|
|   3 |    TABLE ACCESS BY INDEX ROWID| EMPLOYEES  |      1 |      6 |     20 |00:00:00.01 |       2 |      1|       |       |          |
|*  4 |     INDEX RANGE SCAN          | EMP_JOB_IX |      1 |      6 |     20 |00:00:00.01 |       1 |      1|       |       |          |
|   5 |   SORT UNIQUE                 |            |      1 |     50 |     14 |00:00:00.01 |       6 |      0|  3072 |  3072 | 2048  (0)|
|*  6 |    TABLE ACCESS FULL          | EMPLOYEES  |      1 |     50 |     14 |00:00:00.01 |       6 |      0|       |       |          |
------------------------------------------------------------------------------------------------------------------------------------------


Predicate Information (identified by operation id):
---------------------------------------------------

   4 - access("JOB_ID"='ST_CLERK')
   6 - filter("SALARY">10000)





HR@ora11g> select /*+ gather_plan_statistics */ employee_id, last_name from employees where job_id = 'ST_CLERK'
intersect
select employee_id, last_name from employees where salary > 10000;


HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------
SQL_ID  gyh4aznh5q5qp, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ employee_id, last_name from
employees where job_id = 'ST_CLERK' intersect select employee_id,
last_name from employees where salary > 10000

Plan hash value: 189309529

---------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name       | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
---------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |            |      1 |        |      0 |00:00:00.01 |       8 |       |       |          |
|   1 |  INTERSECTION                 |            |      1 |        |      0 |00:00:00.01 |       8 |       |       |          |
|   2 |   SORT UNIQUE                 |            |      1 |      6 |     20 |00:00:00.01 |       2 |  3072 |  3072 | 2048  (0)|
|   3 |    TABLE ACCESS BY INDEX ROWID| EMPLOYEES  |      1 |      6 |     20 |00:00:00.01 |       2 |       |       |          |
|*  4 |     INDEX RANGE SCAN          | EMP_JOB_IX |      1 |      6 |     20 |00:00:00.01 |       1 |       |       |          |
|   5 |   SORT UNIQUE                 |            |      1 |     50 |     14 |00:00:00.01 |       6 |  3072 |  3072 | 2048  (0)|
|*  6 |    TABLE ACCESS FULL          | EMPLOYEES  |      1 |     50 |     14 |00:00:00.01 |       6 |       |       |          |
---------------------------------------------------------------------------------------------------------------------------------


Predicate Information (identified by operation id):
---------------------------------------------------

   4 - access("JOB_ID"='ST_CLERK')
   6 - filter("SALARY">10000)


> 유니온, minus, intersect -> 소트 오퍼레이션 발생
    - union     -> union all + not exists
    - minus     -> not exists
    - intersect -> exists





HR@ora11g> select /*+ gather_plan_statistics reading(d e) use_merge(d) */ e.*, d.*
from employees e, departments d
where e.department_id = d.department_id;

106 rows selected.


HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------
SQL_ID  cssar1xw2zj7u, child number 0
-------------------------------------
select /*+ gather_plan_statistics reading(d e) use_merge(d) */ e.*, d.*
from employees e, departments d where e.department_id = d.department_id

Plan hash value: 1343509718

------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name        | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads |  OMem |  1Mem | Used-Mem |
------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |             |      1 |        |    106 |00:00:00.01 |      18 |      2|       |       |          |
|   1 |  MERGE JOIN                  |             |      1 |    106 |    106 |00:00:00.01 |      18 |      2|       |       |          |
|   2 |   TABLE ACCESS BY INDEX ROWID| DEPARTMENTS |      1 |     27 |     27 |00:00:00.01 |      12 |      2|       |       |          |
|   3 |    INDEX FULL SCAN           | DEPT_ID_PK  |      1 |     27 |     27 |00:00:00.01 |       6 |      1|       |       |          |
|*  4 |   SORT JOIN                  |             |     27 |    107 |    106 |00:00:00.01 |       6 |      0| 16384 | 16384 |14336  (0)|
|   5 |    TABLE ACCESS FULL         | EMPLOYEES   |      1 |    107 |    107 |00:00:00.01 |       6 |      0|       |       |          |
------------------------------------------------------------------------------------------------------------------------------------------


Predicate Information (identified by operation id):
---------------------------------------------------

   4 - access("E"."DEPARTMENT_ID"="D"."DEPARTMENT_ID")
       filter("E"."DEPARTMENT_ID"="D"."DEPARTMENT_ID")



- 인덱스 생성 시 발생
HR@ora11g> create index hr.emp_idx on hr.emp(employee_id);

HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

> 재귀호출 select employee_id 
          from hr.emp 
          order by employee_id;
> 내부 select 문장 작동하여 소트 오퍼레이션 발생



- 통계수집할 때






■ 실행계획
1. data access 방법
2. join 방법
3. join 순서



■ 조인 방법

1. nested loop join
    - 조인의 건수가 적을 때 유리하다.
    - 인덱스를 통해서 데이터를 액세스하는 조인

    - use_nl

 

> random I/O


2. sort merge join
    - 조인되는 건수가 많을 때 유리하다.
    - sort에 대한 성능문제가 발생할 수 있다.
    - use_merge

3. hash join 
    - 조인되는 건수가 많을 때 유리하다.
    - hash 알고리즘이 수행된다.
    - use_hash



■ 조인 순서에 관련된 힌트
1. ordered : from절에 나열된 테이블 순서대로 조인의 순서가 결정된다.
2. leading : leading 힌트 안에 나열한 테이블 순서대로 조인의 순서가 결정된다.


HR@ora11g> select /*+ gather_plan_statistics */ e.last_name, e.salary, e.job_id, d.department_name
           from departments d, employees e
           where d.department_id = e.department_id
           and e.employee_id = 100;

LAST_NAME                     SALARY JOB_ID     DEPARTMENT_NAME
------------------------- ---------- ---------- ------------------------------
King                            2000 AD_PRES    Executive


HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------
SQL_ID  g4aan7ty7ayrh, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ e.last_name, e.salary, e.job_id,
d.department_name from departments d, employees e where d.department_id
= e.department_id and e.employee_id = 100

Plan hash value: 2782876085

--------------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name          | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
--------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |               |      1 |        |      1 |00:00:00.01 |       4 |
|   1 |  NESTED LOOPS                |               |      1 |      1 |      1 |00:00:00.01 |       4 |
|   2 |   TABLE ACCESS BY INDEX ROWID| EMPLOYEES     |      1 |      1 |      1 |00:00:00.01 |       2 |
|*  3 |    INDEX UNIQUE SCAN         | EMP_EMP_ID_PK |      1 |      1 |      1 |00:00:00.01 |       1 |
|   4 |   TABLE ACCESS BY INDEX ROWID| DEPARTMENTS   |      1 |      1 |      1 |00:00:00.01 |       2 |
|*  5 |    INDEX UNIQUE SCAN         | DEPT_ID_PK    |      1 |      1 |      1 |00:00:00.01 |       1 |
--------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("E"."EMPLOYEE_ID"=100)
   5 - access("D"."DEPARTMENT_ID"="E"."DEPARTMENT_ID")





HR@ora11g> select /*+ gather_plan_statistics use_nl(e,d) */ e.last_name, e.salary, e.job_id, d.department_name
           from departments d, employees e
           where d.department_id = e.department_id
           and e.employee_id = 100;

LAST_NAME                     SALARY JOB_ID     DEPARTMENT_NAME
------------------------- ---------- ---------- ------------------------------
King                            2000 AD_PRES    Executive


HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------
SQL_ID  fmdgg9zhun78x, child number 0
-------------------------------------
select /*+ gather_plan_statistics use_nl(e,d) */ e.last_name, e.salary,
e.job_id, d.department_name from departments d, employees e where
d.department_id = e.department_id and e.employee_id = 100

Plan hash value: 2782876085

--------------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name          | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
--------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |               |      1 |        |      1 |00:00:00.01 |       4 |
|   1 |  NESTED LOOPS                |               |      1 |      1 |      1 |00:00:00.01 |       4 |
|   2 |   TABLE ACCESS BY INDEX ROWID| EMPLOYEES     |      1 |      1 |      1 |00:00:00.01 |       2 |    
|*  3 |    INDEX UNIQUE SCAN         | EMP_EMP_ID_PK |      1 |      1 |      1 |00:00:00.01 |       1 |
|   4 |   TABLE ACCESS BY INDEX ROWID| DEPARTMENTS   |      1 |      1 |      1 |00:00:00.01 |       2 |    
|*  5 |    INDEX UNIQUE SCAN         | DEPT_ID_PK    |      1 |      1 |      1 |00:00:00.01 |       1 |
--------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("E"."EMPLOYEE_ID"=100)                    <- 메인, outer table / driving table
   5 - access("D"."DEPARTMENT_ID"="E"."DEPARTMENT_ID")  <- 서브, inner table / driven table


- INDEX UNIQUE SCAN : 1 : 데이터가 작아서 루프~리프가 한곳에 있어 I/O 1번 발생
- 해석 : 3 - 2 - 5 - 4 - 1, 높고 깊은 레벨 부터

employee_id = 100 -> employees -> d.department_id -> departments = I/O 4번





HR@ora11g> select /*+ gather_plan_statistics ordered use_nl(e,d) */ 
                  e.last_name, e.salary, e.job_id, d.department_name
           from departments d, employees e
           where d.department_id = e.department_id
           and e.employee_id = 100;

LAST_NAME                     SALARY JOB_ID     DEPARTMENT_NAME
------------------------- ---------- ---------- ------------------------------
King                            2000 AD_PRES    Executive


HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------
SQL_ID  2hb85gda0v7nk, child number 0
-------------------------------------
select /*+ gather_plan_statistics ordered use_nl(e,d) */ e.last_name,
e.salary, e.job_id, d.department_name            from departments d,
employees e            where d.department_id = e.department_id
  and e.employee_id = 100

Plan hash value: 3818396904

--------------------------------------------------------------------------------------------------------------------------
| Id | Operation                         | Name              | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads |
--------------------------------------------------------------------------------------------------------------------------
|  0 | SELECT STATEMENT                  |                   |      1 |        |      1 |00:00:00.01 |      16 |     5 |
|  1 |  NESTED LOOPS                     |                   |      1 |      1 |      1 |00:00:00.01 |      16 |     5 |
|  2 |   NESTED LOOPS                    |                   |      1 |    270 |      1 |00:00:00.01 |      15 |     5 |
|  3 |    TABLE ACCESS FULL              | DEPARTMENTS       |      1 |     27 |     27 |00:00:00.01 |       7 |     4 |
|  4 |    BITMAP CONVERSION TO ROWIDS    |                   |     27 |        |      1 |00:00:00.01 |       8 |     1 |
|  5 |     BITMAP AND                    |                   |     27 |        |      1 |00:00:00.01 |       8 |     1 |
|  6 |      BITMAP CONVERSION FROM ROWIDS|                   |     27 |        |     27 |00:00:00.01 |       4 |     0 |
|* 7 |       INDEX RANGE SCAN            | EMP_EMP_ID_PK     |     27 |     10 |     27 |00:00:00.01 |       4 |     0 |
|  8 |      BITMAP CONVERSION FROM ROWIDS|                   |     27 |        |      6 |00:00:00.01 |       4 |     1 |
|* 9 |       INDEX RANGE SCAN            | EMP_DEPARTMENT_IX |     27 |     10 |    106 |00:00:00.01 |       4 |     1 |
| 10 |   TABLE ACCESS BY INDEX ROWID     | EMPLOYEES         |      1 |      1 |      1 |00:00:00.01 |       1 |     0 |
--------------------------------------------------------------------------------------------------------------------------


Predicate Information (identified by operation id):
---------------------------------------------------

   7 - access("E"."EMPLOYEE_ID"=100)
   9 - access("D"."DEPARTMENT_ID"="E"."DEPARTMENT_ID")

- ordered : from 절 나열된 테이블 순서대로
- use_nl(d) : 이너쪽 테이블만 기술





HR@ora11g> select /*+ gather_plan_statistics leading(e,d) use_nl(d) */ 
                  e.last_name, e.salary, e.job_id, d.department_name
           from departments d, employees e
           where d.department_id = e.department_id
           and e.employee_id = 100;

LAST_NAME                     SALARY JOB_ID     DEPARTMENT_NAME
------------------------- ---------- ---------- ------------------------------
King                            2000 AD_PRES    Executive


HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------
SQL_ID  2891shwry3v9x, child number 0
-------------------------------------
select /*+ gather_plan_statistics leading(e,d) use_nl(d) */
e.last_name, e.salary, e.job_id, d.department_name            from
departments d, employees e            where d.department_id =
e.department_id            and e.employee_id = 100

Plan hash value: 2782876085

--------------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name          | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
--------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |               |      1 |        |      1 |00:00:00.01 |       4 |
|   1 |  NESTED LOOPS                |               |      1 |      1 |      1 |00:00:00.01 |       4 |
|   2 |   TABLE ACCESS BY INDEX ROWID| EMPLOYEES     |      1 |      1 |      1 |00:00:00.01 |       2 |
|*  3 |    INDEX UNIQUE SCAN         | EMP_EMP_ID_PK |      1 |      1 |      1 |00:00:00.01 |       1 |
|   4 |   TABLE ACCESS BY INDEX ROWID| DEPARTMENTS   |      1 |      1 |      1 |00:00:00.01 |       2 |
|*  5 |    INDEX UNIQUE SCAN         | DEPT_ID_PK    |      1 |      1 |      1 |00:00:00.01 |       1 |
--------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("E"."EMPLOYEE_ID"=100)
   5 - access("D"."DEPARTMENT_ID"="E"."DEPARTMENT_ID")

- leading(e,d) from 절 순서 상관없이 (아우터,이너) 순서를 표시
- use_nl(d) : 이너 테이블 표시






HR@ora11g> select /*+ gather_plan_statistics leading(e,d,l) use_nl(d,l) */ 
           e.last_name, e.salary, e.job_id, d.department_name, l.city
           from departments d, employees e, locations l
           where d.department_id = e.department_id
           and d.location_id = l.location_id
           and e.employee_id = 100;

LAST_NAME                     SALARY JOB_ID     DEPARTMENT_NAME                CITY
------------------------- ---------- ---------- ------------------------------ ------------------------------
King                            2000 AD_PRES    Executive                      Seattle


HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------
SQL_ID  6anfpughbgxy6, child number 0
-------------------------------------
select /*+ gather_plan_statistics leading(e,d,l) use_nl(d,l) */
e.last_name, e.salary, e.job_id, d.department_name, l.city
from departments d, employees e, locations l            where
d.department_id = e.department_id            and d.location_id =
l.location_id            and e.employee_id = 100

Plan hash value: 2352596526

----------------------------------------------------------------------------------------------------------------
| Id | Operation                     | Name          | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads |
----------------------------------------------------------------------------------------------------------------
|  0 | SELECT STATEMENT              |               |      1 |        |      1 |00:00:00.01 |       6 |     2 |
|  1 |  NESTED LOOPS                 |               |      1 |      1 |      1 |00:00:00.01 |       6 |     2 |
|  2 |   NESTED LOOPS                |               |      1 |      1 |      1 |00:00:00.01 |       4 |     0 |
|  3 |    TABLE ACCESS BY INDEX ROWID| EMPLOYEES     |      1 |      1 |      1 |00:00:00.01 |       2 |     0 |
|* 4 |     INDEX UNIQUE SCAN         | EMP_EMP_ID_PK |      1 |      1 |      1 |00:00:00.01 |       1 |     0 |
|  5 |    TABLE ACCESS BY INDEX ROWID| DEPARTMENTS   |      1 |      1 |      1 |00:00:00.01 |       2 |     0 |
|* 6 |     INDEX UNIQUE SCAN         | DEPT_ID_PK    |      1 |      1 |      1 |00:00:00.01 |       1 |     0 |
|  7 |   TABLE ACCESS BY INDEX ROWID | LOCATIONS     |      1 |      1 |      1 |00:00:00.01 |       2 |     2 |
|* 8 |    INDEX UNIQUE SCAN          | LOC_ID_PK     |      1 |      1 |      1 |00:00:00.01 |       1 |     1 |
----------------------------------------------------------------------------------------------------------------


Predicate Information (identified by operation id):
---------------------------------------------------

   4 - access("E"."EMPLOYEE_ID"=100)
   6 - access("D"."DEPARTMENT_ID"="E"."DEPARTMENT_ID")
   8 - access("D"."LOCATION_ID"="L"."LOCATION_ID")


- leading(e,d,l) use_nl(d,l) = leading(e,d,l) use_nl(d) use_nl(l)

- NESTED LOOPS 2번 : 안쪽부터 수행
- 4 - 3 - 6 - 5 - 2 - 8 - 7 - 1 - 0





1. 테이블의 인덱스 체크
HR@ora11g> select * from user_ind_columns where table_name in ('EMPLOYEES','DEPARTMENTS','LOCATIONS');

INDEX_NAME                     TABLE_NAME           COLUMN_NAME          COLUMN_POSITION COLUMN_LENGTH CHAR_LENGTH DESCEND
------------------------------ -------------------- -------------------- --------------- ------------- ----------- -------
DEPT_ID_PK                     DEPARTMENTS          DEPARTMENT_ID                      1            22           0 ASC
DEPT_LOCATION_IX               DEPARTMENTS          LOCATION_ID                        1            22           0 ASC


2. 비조인조건술어에 해당하는 데이터의 건수
HR@ora11g> select count(*) from hr.departments where location_id = 2500;

  COUNT(*)
----------
         1


HR@ora11g> select /*+ gather_plan_statistics leading(d,e) use_nl(e) */ 
                  e.last_name, e.salary, e.job_id, d.department_name
           from departments d, employees e
           where e.department_id = d.department_id
           and d.location_id = 2500;

34 rows selected.


HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------
SQL_ID  0hafsb9tm89tm, child number 0
-------------------------------------
select /*+ gather_plan_statistics leading(d,e) use_nl(e) */
      e.last_name, e.salary, e.job_id, d.department_name
from departments d, employees e            where e.department_id =
d.department_id            and d.location_id = 2500

Plan hash value: 432925905

-------------------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name              | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |                   |      1 |        |     34 |00:00:00.01 |      11 |
|   1 |  NESTED LOOPS                 |                   |      1 |     10 |     34 |00:00:00.01 |      11 |
|   2 |   NESTED LOOPS                |                   |      1 |     10 |     34 |00:00:00.01 |       7 |
|   3 |    TABLE ACCESS BY INDEX ROWID| DEPARTMENTS       |      1 |      1 |      1 |00:00:00.01 |       3 |
|*  4 |     INDEX RANGE SCAN          | DEPT_LOCATION_IX  |      1 |      1 |      1 |00:00:00.01 |       2 |
|*  5 |    INDEX RANGE SCAN           | EMP_DEPARTMENT_IX |      1 |     10 |     34 |00:00:00.01 |       4 |
|   6 |   TABLE ACCESS BY INDEX ROWID | EMPLOYEES         |     34 |     10 |     34 |00:00:00.01 |       4 |
-------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   4 - access("D"."LOCATION_ID"=2500)
   5 - access("E"."DEPARTMENT_ID"="D"."DEPARTMENT_ID")


outer : DEPARTMENTS

inner : EMPLOYEES

이너쪽 인덱스 EMP_DEPARTMENT_IX 부터 조인 > batch I/O

 

read2, buffer 2 : 디스크에서 읽으면서 메모리에 올림 총 2번 io 발생




■ table prefetch (10g)
- 디스크 I/O를 수행하려면 비용이 많이 들기 때문에 한번에 I/O call이 필요한 시점에 곧이어 읽을 가능성이 높은
block들을 data buffer cache에 미리 적재해 두는 기능
- inner쪽에 non unique index를 range scan 시에 발생한다.
- index range scan은 single block 발생하는 이때 발생하는 이벤트 db file sequential read 이벤트가 발생(피지컬IO)할 수 있는데 table prefetch 기능이 수행되면 db file paralle reads 이벤트가 발생한다.


# optimizer_features_enable('10.2.0.5') : 옵티마이저 버전 낮추기

HR@ora11g> select /*+ optimizer_features_enable('10.2.0.5') 
                      gather_plan_statistics
                      leading(d,e)
                      use_nl(e)
                  */
                  e.last_name, e.salary, e.job_id, d.department_name
           from departments d, employees e
           where e.department_id = d.department_id
           and d.location_id = 2500;

34 rows selected.

- outer : 
- inner : 



HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------
SQL_ID  268aj816su8bz, child number 0
-------------------------------------
select /*+ optimizer_features_enable('10.2.0.5')
gather_plan_statistics                       leading(d,e)
        use_nl(e)                   */                   e.last_name,
e.salary, e.job_id, d.department_name            from departments d,
employees e            where e.department_id = d.department_id
  and d.location_id = 2500

Plan hash value: 1096811146

-------------------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name              | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |                   |      0 |        |      0 |00:00:00.01 |       0 |
|   1 |  TABLE ACCESS BY INDEX ROWID  | EMPLOYEES         |      1 |     10 |     34 |00:00:00.01 |      11 |
|   2 |   NESTED LOOPS                |                   |      1 |     10 |     36 |00:00:00.01 |       7 |
|   3 |    TABLE ACCESS BY INDEX ROWID| DEPARTMENTS       |      1 |      1 |      1 |00:00:00.01 |       3 |
|*  4 |     INDEX RANGE SCAN          | DEPT_LOCATION_IX  |      1 |      1 |      1 |00:00:00.01 |       2 |
|*  5 |    INDEX RANGE SCAN           | EMP_DEPARTMENT_IX |      1 |     10 |     34 |00:00:00.01 |       4 |
-------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   4 - access("D"."LOCATION_ID"=2500)
   5 - access("E"."DEPARTMENT_ID"="D"."DEPARTMENT_ID")


# 옵티마이저 버전 찾기
HR@ora11g> alter session set optimizer_features_enable = '9.2.100';
ERROR:
ORA-00096: invalid value 9.2.100 for parameter optimizer_features_enable, must be from among 11.2.0.4.1,
11.2.0.4, 11.2.0.3, 11.2.0.2, 11.2.0.1, 11.1.0.7, 11.1.0.6, 10.2.0.5, 10.2.0.4, 10.2.0.3, 10.2.0.2, 10.2.0.1,
10.1.0.5, 10.1.0.4, 10.1.0.3, 10.1.0, 9.2.0.8, 9.2.0, 9.0.1, 9.0.0, 8.1.7, 8.1.6, 8.1.5, 8.1.4, 8.1.3, 8.1.0,
8.0.7, 8.0.6, 8.0.5, 8.0.4, 8.0.3, 8.0.0





HR@ora11g> select /*+ optimizer_features_enable('9.0.0') 
                      gather_plan_statistics
                      leading(d,e)
                      use_nl(e)
                  */
                  e.last_name, e.salary, e.job_id, d.department_name
           from departments d, employees e
           where e.department_id = d.department_id
           and d.location_id = 2500;

34 rows selected.


HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------
SQL_ID  88wq86y38ss7y, child number 0
-------------------------------------
select /*+ optimizer_features_enable('9.0.0')
gather_plan_statistics                       leading(d,e)
        use_nl(e)                   */                   e.last_name,
e.salary, e.job_id, d.department_name            from departments d,
employees e            where e.department_id = d.department_id
  and d.location_id = 2500

Plan hash value: 2912831499

-----------------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name              | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |                   |      0 |        |      0 |00:00:00.01 |       0 |
|   1 |  TABLE ACCESS BY INDEX ROWID| EMPLOYEES         |      1 |     10 |     34 |00:00:00.01 |      15 |
|   2 |   NESTED LOOPS              |                   |      1 |      5 |     36 |00:00:00.01 |      11 |
|*  3 |    TABLE ACCESS FULL        | DEPARTMENTS       |      1 |      1 |      1 |00:00:00.01 |       7 |
|*  4 |    INDEX RANGE SCAN         | EMP_DEPARTMENT_IX |      1 |     10 |     34 |00:00:00.01 |       4 |
-----------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - filter("D"."LOCATION_ID"=2500)
   4 - access("E"."DEPARTMENT_ID"="D"."DEPARTMENT_ID")

Note
-----
   - cpu costing is off (consider enabling it)






HR@ora11g> select /*+ optimizer_features_enable('8.0.0') 
                      gather_plan_statistics
                      leading(d,e)
                      use_nl(e)
                  */
                  e.last_name, e.salary, e.job_id, d.department_name
           from departments d, employees e
           where e.department_id = d.department_id
           and d.location_id = 2500;

34 rows selected.


HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------
SQL_ID  1af2nkd13v4gt, child number 0
-------------------------------------
select /*+ optimizer_features_enable('8.0.0')
gather_plan_statistics                       leading(d,e)
        use_nl(e)                   */                   e.last_name,
e.salary, e.job_id, d.department_name            from departments d,
employees e            where e.department_id = d.department_id
  and d.location_id = 2500

Plan hash value: 2912831499

-----------------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name              | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |                   |      0 |        |      0 |00:00:00.01 |       0 |
|   1 |  TABLE ACCESS BY INDEX ROWID| EMPLOYEES         |      1 |     10 |     34 |00:00:00.01 |      15 |
|   2 |   NESTED LOOPS              |                   |      1 |     10 |     36 |00:00:00.01 |      11 |
|*  3 |    TABLE ACCESS FULL        | DEPARTMENTS       |      1 |      1 |      1 |00:00:00.01 |       7 |
|*  4 |    INDEX RANGE SCAN         | EMP_DEPARTMENT_IX |      1 |     10 |     34 |00:00:00.01 |       4 |
-----------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - filter("D"."LOCATION_ID"=2500)
   4 - access("E"."DEPARTMENT_ID"="D"."DEPARTMENT_ID")

Note
-----
   - cpu costing is off (consider enabling it)






■ batch I/O
- inner쪽 인덱스와 조인하면서 중간 결과 집합을 만든 후에 inner 쪽 테이블괄 일괄(batch) 처리한다.
- 힌트 : nlj_batching, no_nlj_batching

HR@ora11g> select /*+ gather_plan_statistics
                      leading(d,e)
                      no_nlj_batching(e)
                  */
                  e.last_name, e.salary, e.job_id, d.department_name
           from departments d, employees e
           where e.department_id = d.department_id
           and d.location_id = 2500;

34 rows selected.


HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
...
-------------------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name              | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |                   |      0 |        |      0 |00:00:00.01 |       0 |
|   1 |  TABLE ACCESS BY INDEX ROWID  | EMPLOYEES         |      1 |     10 |     34 |00:00:00.01 |      11 |
|   2 |   NESTED LOOPS                |                   |      1 |     10 |     36 |00:00:00.01 |       7 |
|   3 |    TABLE ACCESS BY INDEX ROWID| DEPARTMENTS       |      1 |      1 |      1 |00:00:00.01 |       3 |
|*  4 |     INDEX RANGE SCAN          | DEPT_LOCATION_IX  |      1 |      1 |      1 |00:00:00.01 |       2 |
|*  5 |    INDEX RANGE SCAN           | EMP_DEPARTMENT_IX |      1 |     10 |     34 |00:00:00.01 |       4 |
-------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   4 - access("D"."LOCATION_ID"=2500)
   5 - access("E"."DEPARTMENT_ID"="D"."DEPARTMENT_ID")

> table prefetch