# 요약 | |
■ 조건절 pushdown ■ 조건절 pullup # union # union all # lnnvl ■ or - expansion # use_concat # no_expand _remove_aggr_subquery ■ optimazer 1. Rule Based Optimization(RBO) 2. Cost Based Optimization(CBO) ■ optimizer_mode - choose - rule - all_rows - first_rows - first_rows_n(1,10,100,1000) SQL문 > PASER > Query Transformer > Estimator > plan Generator 1. Query Transformer 2. Estimator - selectivily - cardinality - cost 3. Plan Generator ■ optimizer에게 영향을 주는 요소 ■ Histogram |
■ 조건절 pushdown
- group by 절에 포함된 복합 뷰 merging에 실패 했을 경우에 쿼리 블록 밖에 있는 조건절을 쿼리 블록 안쪽으로 넣음으로써 group by 해야할 데이터 양을 줄일 수 있다.
HR@ora11g> select /*+ gather_plan_statistics */ *
from (select department_id, sum(salary) sum_sal
from employees
group by department_id)
where department_id = 20;
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 | | 1 | | 1 |00:00:00.01 | 2 |
| 1 | SORT GROUP BY NOSORT | | 1 | 1 | 1 |00:00:00.01 | 2 |
| 2 | TABLE ACCESS BY INDEX ROWID| EMPLOYEES | 1 | 2 | 2 |00:00:00.01 | 2 |
|* 3 | INDEX RANGE SCAN | EMP_DEPARTMENT_IX | 1 | 2 | 2 |00:00:00.01 | 1 |
------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("DEPARTMENT_ID"=20)
> 불필요한 I/O 유발하는 SQL문 > Transformation 작동
■ 조건절 pullup
- 조건절을 쿼리 블록 안으로 밀어 넣을 뿐만 아니라 안쪽에 있는 조건들을 바깥쪽으로 끄집어 내기도하는데 이를 조건절 pullup 이라고 한다.
HR@ora11g> select /*+ gather_plan_statistics */ a.department_id, a.sum_sal, b.max_sal, b.min_sal
from (select department_id, sum(salary) sum_sal
from employees
where department_id = 20
group by department_id) a,
(select department_id, max(salary) max_sal, min(salary) min_sal
from employees
group by department_id) b
where a.department_id = b.department_id;
HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
-----------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem |
-----------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 4 | | | |
|* 1 | HASH JOIN | | 1 | 1 | 1 |00:00:00.01 | 4 | 1280K| 1280K| 375K (0)|
| 2 | VIEW | | 1 | 1 | 1 |00:00:00.01 | 2 | | | |
| 3 | HASH GROUP BY | | 1 | 1 | 1 |00:00:00.01 | 2 | 943K| 943K| 691K (0)|
| 4 | TABLE ACCESS BY INDEX ROWID| EMPLOYEES | 1 | 2 | 2 |00:00:00.01 | 2 | | | |
|* 5 | INDEX RANGE SCAN | EMP_DEPARTMENT_IX | 1 | 2 | 2 |00:00:00.01 | 1 | | | |
| 6 | VIEW | | 1 | 1 | 1 |00:00:00.01 | 2 | | | |
| 7 | HASH GROUP BY | | 1 | 1 | 1 |00:00:00.01 | 2 | 833K| 833K| 694K (0)|
| 8 | TABLE ACCESS BY INDEX ROWID| EMPLOYEES | 1 | 2 | 2 |00:00:00.01 | 2 | | | |
|* 9 | INDEX RANGE SCAN | EMP_DEPARTMENT_IX | 1 | 2 | 2 |00:00:00.01 | 1 | | | |
-----------------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("A"."DEPARTMENT_ID"="B"."DEPARTMENT_ID")
5 - access("DEPARTMENT_ID"=20)
9 - access("DEPARTMENT_ID"=20)
|| 동일
HR@ora11g> select /*+ gather_plan_statistics */ a.department_id, a.sum_sal, b.max_sal, b.min_sal
from (select department_id, sum(salary) sum_sal
from employees
where department_id = 20
group by department_id) a,
(select department_id, max(salary) max_sal, min(salary) min_sal
from employees
where department_id = 20 --
group by department_id) b
where a.department_id = b.department_id;
> 결국은 department_id = 20 구하기
# 대용량 테이블 + Full Scan + OR
HR@ora11g> select /*+ gather_plan_statistics */ *
from employees
where job_id = 'IT_PROG' or department_id = 20;
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 | | 1 | | 7 |00:00:00.01 | 7 |
|* 1 | TABLE ACCESS FULL| EMPLOYEES | 1 | 8 | 7 |00:00:00.01 | 7 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(("JOB_ID"='IT_PROG' OR "DEPARTMENT_ID"=20))
> or 사용으로 Full Scan
↓ 분리
HR@ora11g> select /*+ gather_plan_statistics */ *
from employees
where job_id = 'IT_PROG';
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 | | 1 | | 5 |00:00:00.01 | 4 |
| 1 | TABLE ACCESS BY INDEX ROWID| EMPLOYEES | 1 | 6 | 5 |00:00:00.01 | 4 |
|* 2 | INDEX RANGE SCAN | EMP_JOB_IX | 1 | 6 | 5 |00:00:00.01 | 2 |
----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("JOB_ID"='IT_PROG')
+
HR@ora11g> select /*+ gather_plan_statistics */ *
from employees
where department_id = 20;
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 | | 1 | | 2 |00:00:00.01 | 4 |
| 1 | TABLE ACCESS BY INDEX ROWID| EMPLOYEES | 1 | 2 | 2 |00:00:00.01 | 4 |
|* 2 | INDEX RANGE SCAN | EMP_DEPARTMENT_IX | 1 | 2 | 2 |00:00:00.01 | 2 |
-----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("DEPARTMENT_ID"=20)
↓ or 보다는 union
# union
HR@ora11g> select /*+ gather_plan_statistics */ *
from employees
where job_id = 'IT_PROG'
union
select *
from employees
where department_id = 20;
HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
----------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem |
----------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 7 |00:00:00.01 | 4 | | | |
| 1 | SORT UNIQUE | | 1 | 8 | 7 |00:00:00.01 | 4 | 2048 | 2048 | 2048 (0)|
| 2 | UNION-ALL | | 1 | | 7 |00:00:00.01 | 4 | | | |
| 3 | TABLE ACCESS BY INDEX ROWID| EMPLOYEES | 1 | 6 | 5 |00:00:00.01 | 2 | | | |
|* 4 | INDEX RANGE SCAN | EMP_JOB_IX | 1 | 6 | 5 |00:00:00.01 | 1 | | | |
| 5 | TABLE ACCESS BY INDEX ROWID| EMPLOYEES | 1 | 2 | 2 |00:00:00.01 | 2 | | | |
|* 6 | INDEX RANGE SCAN | EMP_DEPARTMENT_IX | 1 | 2 | 2 |00:00:00.01 | 1 | | | |
----------------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("JOB_ID"='IT_PROG')
6 - access("DEPARTMENT_ID"=20)
> 단점은 sort 작동
# union all
HR@ora11g> select /*+ gather_plan_statistics */ *
from employees
where job_id = 'IT_PROG'
union all
select *
from employees
where department_id = 20;
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 | | 1 | | 7 |00:00:00.01 | 6 |
| 1 | UNION-ALL | | 1 | | 7 |00:00:00.01 | 6 |
| 2 | TABLE ACCESS BY INDEX ROWID| EMPLOYEES | 1 | 6 | 5 |00:00:00.01 | 4 |
|* 3 | INDEX RANGE SCAN | EMP_JOB_IX | 1 | 6 | 5 |00:00:00.01 | 2 |
| 4 | TABLE ACCESS BY INDEX ROWID| EMPLOYEES | 1 | 2 | 2 |00:00:00.01 | 2 |
|* 5 | INDEX RANGE SCAN | EMP_DEPARTMENT_IX | 1 | 2 | 2 |00:00:00.01 | 1 |
------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("JOB_ID"='IT_PROG')
5 - access("DEPARTMENT_ID"=20)
-> 단점은 중복데이터 발생
↓ 중복데이터 제외
HR@ora11g> select /*+ gather_plan_statistics */ *
from employees
where job_id = 'IT_PROG'
and (department_id <> 20 or department_id is null);
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 | | 1 | | 5 |00:00:00.01 | 4 |
|* 1 | TABLE ACCESS BY INDEX ROWID| EMPLOYEES | 1 | 5 | 5 |00:00:00.01 | 4 |
|* 2 | INDEX RANGE SCAN | EMP_JOB_IX | 1 | 6 | 5 |00:00:00.01 | 2 |
----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(("DEPARTMENT_ID"<>20 OR "DEPARTMENT_ID" IS NULL))
2 - access("JOB_ID"='IT_PROG')
> 우선순위 주의 : and > or
+
# lnnvl : 부정 or null값 찾기
HR@ora11g> select /*+ gather_plan_statistics */ *
from employees
where job_id = 'IT_PROG'
and lnnvl(department_id = 20); -- ("DEPARTMENT_ID"<>20 OR "DEPARTMENT_ID" IS NULL)
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 | | 1 | | 5 |00:00:00.01 | 4 |
|* 1 | TABLE ACCESS BY INDEX ROWID| EMPLOYEES | 1 | 6 | 5 |00:00:00.01 | 4 |
|* 2 | INDEX RANGE SCAN | EMP_JOB_IX | 1 | 6 | 5 |00:00:00.01 | 2 |
----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(LNNVL("DEPARTMENT_ID"=20)
2 - access("JOB_ID"='IT_PROG')
↓ 개선
HR@ora11g> select /*+ gather_plan_statistics */ *
from employees
where job_id = 'IT_PROG'
and lnnvl(department_id = 20)
union all
select *
from employees
where department_id = 20;
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 | | 1 | | 7 |00:00:00.01 | 6 |
| 1 | UNION-ALL | | 1 | | 7 |00:00:00.01 | 6 |
|* 2 | TABLE ACCESS BY INDEX ROWID| EMPLOYEES | 1 | 6 | 5 |00:00:00.01 | 4 |
|* 3 | INDEX RANGE SCAN | EMP_JOB_IX | 1 | 6 | 5 |00:00:00.01 | 2 |
| 4 | TABLE ACCESS BY INDEX ROWID| EMPLOYEES | 1 | 2 | 2 |00:00:00.01 | 2 |
|* 5 | INDEX RANGE SCAN | EMP_DEPARTMENT_IX | 1 | 2 | 2 |00:00:00.01 | 1 |
------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter(LNNVL("DEPARTMENT_ID"=20))
3 - access("JOB_ID"='IT_PROG')
5 - access("DEPARTMENT_ID"=20)
|| 동일. lnnvl 사용하지 않은 SQL문
HR@ora11g> select /*+ gather_plan_statistics */ *
from employees
where job_id = 'IT_PROG'
and (department_id <> 20 or department_id is null)
union all
select *
from employees
where department_id = 20;
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 | | 1 | | 7 |00:00:00.01 | 6 |
| 1 | UNION-ALL | | 1 | | 7 |00:00:00.01 | 6 |
|* 2 | TABLE ACCESS BY INDEX ROWID| EMPLOYEES | 1 | 5 | 5 |00:00:00.01 | 4 |
|* 3 | INDEX RANGE SCAN | EMP_JOB_IX | 1 | 6 | 5 |00:00:00.01 | 2 |
| 4 | TABLE ACCESS BY INDEX ROWID| EMPLOYEES | 1 | 2 | 2 |00:00:00.01 | 2 |
|* 5 | INDEX RANGE SCAN | EMP_DEPARTMENT_IX | 1 | 2 | 2 |00:00:00.01 | 1 |
------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter(("DEPARTMENT_ID"<>20 OR "DEPARTMENT_ID" IS NULL))
3 - access("JOB_ID"='IT_PROG')
5 - access("DEPARTMENT_ID"=20)
■ or - expansion
- 사용자의 쿼리를 직접 바꾸지 않아도 옵티마이저가 or 조건을 full scan 처리가 아닌 index range scan을 수행하면서
union all 형태로 변경 처리해주는 기능
- Hint : use_concat : or - expansion을 유도한다.
no_expand : or - expansion을 유도하지 않겠다. -> Full Scan
# 기본
HR@ora11g> select /*+ gather_plan_statistics */ *
from employees
where job_id = 'IT_PROG' or department_id = 20;
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 | | 1 | | 7 |00:00:00.01 | 7 |
|* 1 | TABLE ACCESS FULL| EMPLOYEES | 1 | 8 | 7 |00:00:00.01 | 7 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(("JOB_ID"='IT_PROG' OR "DEPARTMENT_ID"=20))
> job_id, department_id 인덱스가 걸려있는데 or 사용으로 Full Scan
# use_concat
HR@ora11g> select /*+ gather_plan_statistics use_concat */ *
from employees
where job_id = 'IT_PROG' or department_id = 20;
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 | | 1 | | 7 |00:00:00.01 | 6 |
| 1 | CONCATENATION | | 1 | | 7 |00:00:00.01 | 6 |
| 2 | TABLE ACCESS BY INDEX ROWID| EMPLOYEES | 1 | 2 | 2 |00:00:00.01 | 4 |
|* 3 | INDEX RANGE SCAN | EMP_DEPARTMENT_IX | 1 | 2 | 2 |00:00:00.01 | 2 |
|* 4 | TABLE ACCESS BY INDEX ROWID| EMPLOYEES | 1 | 6 | 5 |00:00:00.01 | 2 |
|* 5 | INDEX RANGE SCAN | EMP_JOB_IX | 1 | 6 | 5 |00:00:00.01 | 1 |
------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("DEPARTMENT_ID"=20)
4 - filter(LNNVL("DEPARTMENT_ID"=20))
5 - access("JOB_ID"='IT_PROG')
> CONCATENATION : union all 수행
# no_expand
HR@ora11g> select /*+ gather_plan_statistics no_expand */ *
from employees
where job_id = 'IT_PROG' or department_id = 20;
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 | | 1 | | 7 |00:00:00.01 | 7 |
|* 1 | TABLE ACCESS FULL| EMPLOYEES | 1 | 8 | 7 |00:00:00.01 | 7 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(("JOB_ID"='IT_PROG' OR "DEPARTMENT_ID"=20))
> 데이터 분포도에 따른 영향으로 인덱스 스캔이 무조건 좋은것만은 아니다.
HR@ora11g> select department_id, count(*) from hr.employees group by department_id order by 2 desc;
DEPARTMENT_ID COUNT(*)
------------- ----------
50 45
80 34
100 6
30 6
60 5
90 3
110 2
20 2
40 1
1
10 1
70 1
> 값의 분포도가 고르지않다.
# 직업별 그룹화
HR@ora11g> select job_id, count(*) from hr.employees group by job_id order by 2 desc
19 rows selected.
> 해당 데이터도 값의 분포도가 고르지않다.
HR@ora11g> select /*+ gather_plan_statistics */ *
from employees
where job_id = 'IT_PROG' or department_id = 50;
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 | | 1 | | 50 |00:00:00.01 | 10 |
|* 1 | TABLE ACCESS FULL| EMPLOYEES | 1 | 48 | 50 |00:00:00.01 | 10 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(("DEPARTMENT_ID"=50 OR "JOB_ID"='IT_PROG'))
HR@ora11g> select department_id, count(*) from hr.employees group by department_id order by 2 desc;
DEPARTMENT_ID COUNT(*)
------------- ----------
50 45
80 34
100 6
30 6
60 5
90 3
110 2
20 2
40 1
1
10 1
70 1
> 값의 분포도가 고르지않다.
HR@ora11g> select job_id, count(*) from hr.employees group by job_id order by 2 desc
19 rows selected.
> 해당 데이터도 값의 분포도가 고르지않다.
↓
# use_concat
HR@ora11g> select /*+ gather_plan_statistics use_concat */ *
from employees
where job_id = 'IT_PROG' or department_id = 50;
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 | | 1 | | 50 |00:00:00.01 | 13 |
| 1 | CONCATENATION | | 1 | | 50 |00:00:00.01 | 13 |
| 2 | TABLE ACCESS BY INDEX ROWID| EMPLOYEES | 1 | 6 | 5 |00:00:00.01 | 4 |
|* 3 | INDEX RANGE SCAN | EMP_JOB_IX | 1 | 6 | 5 |00:00:00.01 | 2 |
|* 4 | TABLE ACCESS FULL | EMPLOYEES | 1 | 43 | 45 |00:00:00.01 | 9 |
-----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("JOB_ID"='IT_PROG')
4 - filter(("DEPARTMENT_ID"=50 AND LNNVL("JOB_ID"='IT_PROG')))
> 값의 분포도가 좋지 않아 오라클이 Full Scan 작동
↓
# union all
HR@ora11g> select /*+ gather_plan_statistics */ *
from employees
where job_id = 'IT_PROG'
and lnnvl(department_id = 50)
union all
select /*+ index(e EMP_DEPARTMENT_IX) */ *
from employees
where department_id = 50;
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 | | 1 | | 50 |00:00:00.01 | 13 |
| 1 | UNION-ALL | | 1 | | 50 |00:00:00.01 | 13 |
|* 2 | TABLE ACCESS BY INDEX ROWID| EMPLOYEES | 1 | 3 | 5 |00:00:00.01 | 4 |
|* 3 | INDEX RANGE SCAN | EMP_JOB_IX | 1 | 6 | 5 |00:00:00.01 | 2 |
|* 4 | TABLE ACCESS FULL | EMPLOYEES | 1 | 45 | 45 |00:00:00.01 | 9 |
-----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter(LNNVL("DEPARTMENT_ID"=50))
3 - access("JOB_ID"='IT_PROG')
4 - filter("DEPARTMENT_ID"=50)
> 데이터가 적은 테이블은 Full Scan이 더 낫다
HR@ora11g> select /*+ gather_plan_statistics */ *
from employees
where department_id = 10;
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 | | 1 | | 1 |00:00:00.01 | 3 |
| 1 | TABLE ACCESS BY INDEX ROWID| EMPLOYEES | 1 | 1 | 1 |00:00:00.01 | 3 |
|* 2 | INDEX RANGE SCAN | EMP_DEPARTMENT_IX | 1 | 1 | 1 |00:00:00.01 | 2 |
-----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("DEPARTMENT_ID"=10)
HR@ora11g> select /*+ gather_plan_statistics index(e EMP_DEPARTMENT_IX) */ *
from employees e
where department_id = 50;
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 | | 1 | | 45 |00:00:00.01 | 9 |
| 1 | TABLE ACCESS BY INDEX ROWID| EMPLOYEES | 1 | 45 | 45 |00:00:00.01 | 9 |
|* 2 | INDEX RANGE SCAN | EMP_DEPARTMENT_IX | 1 | 45 | 45 |00:00:00.01 | 4 |
-----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("DEPARTMENT_ID"=50)
# 자신의 부서 평균 급여보다 더 많이 받는 사원들의 employee_id, salary, department_name을 출력해주세요.
HR@ora11g> select /*+ gather_plan_statistics */ e.employee_id, e.salary, d.department_name
from employees e, departments d
where e.department_id = d.department_id
and e.salary > (select avg(salary)
from employees
where department_id = d.department_id);
HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
-----------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem |
-----------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 39 |00:00:00.01 | 8 | | | |
|* 1 | VIEW | VW_WIF_1 | 1 | 106 | 39 |00:00:00.01 | 8 | | | |
| 2 | WINDOW BUFFER | | 1 | 106 | 106 |00:00:00.01 | 8 | 9216 | 9216 | 8192 (0)|
| 3 | MERGE JOIN | | 1 | 106 | 106 |00:00:00.01 | 8 | | | |
| 4 | TABLE ACCESS BY INDEX ROWID| DEPARTMENTS | 1 | 27 | 27 |00:00:00.01 | 2 | | | |
| 5 | INDEX FULL SCAN | DEPT_ID_PK | 1 | 27 | 27 |00:00:00.01 | 1 | | | |
|* 6 | SORT JOIN | | 27 | 107 | 106 |00:00:00.01 | 6 | 9216 | 9216 | 8192 (0)|
| 7 | TABLE ACCESS FULL | EMPLOYEES | 1 | 107 | 107 |00:00:00.01 | 6 | | | |
-----------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("VW_COL_4" IS NOT NULL)
6 - access("E"."DEPARTMENT_ID"="D"."DEPARTMENT_ID")
filter("E"."DEPARTMENT_ID"="D"."DEPARTMENT_ID")
> WINDOW BUFFER : 분석함수 사용
> transformation 작동
↓
# 히든 파라미터 수정
HR@ora11g> alter session set "_remove_aggr_subquery" = false;
Session altered.
HR@ora11g> select /*+ gather_plan_statistics */ e.employee_id, e.salary, d.department_name
from employees e, departments d
where e.department_id = d.department_id
and e.salary > (select avg(salary)
from employees
where department_id = d.department_id);
HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
----------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem |
----------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 39 |00:00:00.01 | 17 | | | |
|* 1 | HASH JOIN | | 1 | 5 | 39 |00:00:00.01 | 17 | 976K| 976K| 838K (0)|
| 2 | MERGE JOIN | | 1 | 11 | 11 |00:00:00.01 | 8 | | | |
| 3 | TABLE ACCESS BY INDEX ROWID| DEPARTMENTS | 1 | 27 | 27 |00:00:00.01 | 2 | | | |
| 4 | INDEX FULL SCAN | DEPT_ID_PK | 1 | 27 | 27 |00:00:00.01 | 1 | | | |
|* 5 | SORT JOIN | | 27 | 11 | 11 |00:00:00.01 | 6 | 2048 | 2048 | 2048 (0)|
| 6 | VIEW | VW_SQ_1 | 1 | 11 | 12 |00:00:00.01 | 6 | | | |
| 7 | HASH GROUP BY | | 1 | 11 | 12 |00:00:00.01 | 6 | 889K| 889K| 1663K (0)|
| 8 | TABLE ACCESS FULL | EMPLOYEES | 1 | 107 | 107 |00:00:00.01 | 6 | | | |
| 9 | TABLE ACCESS FULL | EMPLOYEES | 1 | 107 | 107 |00:00:00.01 | 9 | | | |
----------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("E"."DEPARTMENT_ID"="D"."DEPARTMENT_ID")
filter("E"."SALARY">"AVG(SALARY)")
5 - access("ITEM_1"="D"."DEPARTMENT_ID")
filter("ITEM_1"="D"."DEPARTMENT_ID")
> 버퍼 늘어남, HASH GROUP BY
|| 동일
HR@ora11g> select /*+ gather_plan_statistics */ e2.employee_id, e2.last_name, d.department_name
from (select department_id, avg(salary) avg_sal
from employees
group by department_id) e1,
employees e2, departments d
where e1.department_id = d.department_id
and e2.department_id = d.department_id
and e2.salary > e1.avg_sal;
↓ 최종
# _remove_aggr_subquery
- crrelate subquery(상호관련서브쿼리)를 사용할 때 분석함수를 사용하여 서브쿼리를 제거한다.(10g)
HR@ora11g> alter session set "_remove_aggr_subquery" = true;
Session altered.
HR@ora11g> select /*+ gather_plan_statistics */ e.employee_id, e.salary, d.department_name
from employees e, departments d
where e.department_id = d.department_id
and e.salary > (select avg(salary)
from employees
where department_id = d.department_id);
HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
-----------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem |
-----------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 39 |00:00:00.01 | 8 | | | |
|* 1 | VIEW | VW_WIF_1 | 1 | 106 | 39 |00:00:00.01 | 8 | | | |
| 2 | WINDOW BUFFER | | 1 | 106 | 106 |00:00:00.01 | 8 | 9216 | 9216 | 8192 (0)|
| 3 | MERGE JOIN | | 1 | 106 | 106 |00:00:00.01 | 8 | | | |
| 4 | TABLE ACCESS BY INDEX ROWID| DEPARTMENTS | 1 | 27 | 27 |00:00:00.01 | 2 | | | |
| 5 | INDEX FULL SCAN | DEPT_ID_PK | 1 | 27 | 27 |00:00:00.01 | 1 | | | |
|* 6 | SORT JOIN | | 27 | 107 | 106 |00:00:00.01 | 6 | 9216 | 9216 | 8192 (0)|
| 7 | TABLE ACCESS FULL | EMPLOYEES | 1 | 107 | 107 |00:00:00.01 | 6 | | | |
-----------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("VW_COL_4" IS NOT NULL)
6 - access("E"."DEPARTMENT_ID"="D"."DEPARTMENT_ID")
filter("E"."DEPARTMENT_ID"="D"."DEPARTMENT_ID")
■ optimazer
- 사용자가 요청한 SQL문을 가장 효율적이고 빠르게 수행할 수 있는 최저 비용의 처리경로를 선택해주는 엔진
1. Rule Based Optimization(RBO)
- 미리 정해진 규칙에 의한 순위에 따라 실행계획을 결정(순위 결정 방식)
- 데이터에 대한 통계 내지는 실제 SQL문을 수행할 경우에 소요될 비용에 대해 교려하지않음
예) 인덱스가 있다면 무조건 사용(테이블 크기, 인덱스의 효율등 무시)
- 10g부터는 RBO 지원 중단
- 조건절 컬럼에 인덱스가 있으면 무조건 인덱스 사용
- order by절에 사용된 컬럼에 인덱스가 있으면 무조건 인덱스 사용
- 부등호 조건의 인덱스 보다 between and 조건의 인덱스가 우선
예) select * from emp
where department_id >= 10 and salary between 1000 and 10000;
> >= 부등호보다 between~and 연산자 우선
> 1순위 Rowid Scan ... 15순위 Full Table Scan
2. Cost Based Optimization(CBO)
- Oracle 7부터 지원
- cost를 기반으로 SQL 최적화 수행
- 실제 SQL을 수행할 때 소요될 비용을 예측하고 그 값을 기준으로 실행 계획을 설정 -> 통계정보 필요
- Object 통계
- 테이블 통계 : dba_tables, sys.tab$
- 컬럼 통계 : dba_tab_columns, sys.col$
- 인덱스 통계 : dba_indexes, sys.ind$
- System 통계
- CPU 속도
- 디스크 I/O
■ optimizer_mode
- choose : 통계 정보가 있으면 all_rows, 통계정보가 없으면 rule (9i 기본값)
- rule : 통계정보와 상관 없이 RBO 사용 중
- all_rows : 전체 처리율의 최적화(10g 기본값), 정확한 결과값이 중요
DSS(의사결정시스템), batch성 업무가 있는 환경, Full Scan 유도
- first_rows : 최초 응답속도 최적화(빨리 찾기), OLTP환경, 인덱스 스캔 선호
- first_rows_n(1,10,100,1000) : 처음 결좌가 나올때까지의 시간을 줄이기 위해 최적화(n = 건)
n이 100을 넘으면 all_rows와 같다
SYS@ora11g> show parameter optimizer_mode
NAME TYPE VALUE
------------------------------------ ------- ------------------------------
optimizer_mode string ALL_ROWS
# 시스템레벨 : first_rows를 하는건 위험성이있다.
alter system set optimzer_mode = first_rows;
# 세션레벨
alter session set optimzer_mode = first_rows_10;
HR@ora11g> select /*+ gather_plan_statistics */ *
from hr.employees
where department_id = 50;
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 | | 1 | | 45 |00:00:00.01 | 9 |
|* 1 | TABLE ACCESS FULL| EMPLOYEES | 1 | 45 | 45 |00:00:00.01 | 9 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("DEPARTMENT_ID"=50)
# first_rows(10)
HR@ora11g> select /*+ gather_plan_statistics first_rows(10) */ *
from hr.employees
where department_id = 50;
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 | | 1 | | 45 |00:00:00.01 | 9 |
| 1 | TABLE ACCESS BY INDEX ROWID| EMPLOYEES | 1 | 10 | 45 |00:00:00.01 | 9 |
|* 2 | INDEX RANGE SCAN | EMP_DEPARTMENT_IX | 1 | | 45 |00:00:00.01 | 4 |
-----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("DEPARTMENT_ID"=50)
# all_rows : 속도무시, 정확한 계획으로 수행하달라.
HR@ora11g> select /*+ gather_plan_statistics all_rows */ *
from employees
where department_id = 50;
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 | | 1 | | 45 |00:00:00.01 | 9 |
|* 1 | TABLE ACCESS FULL| EMPLOYEES | 1 | 45 | 45 |00:00:00.01 | 9 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("DEPARTMENT_ID"=50)
# all_rows(10) : 10건을 최대한 빨리 찾아라
HR@ora11g> select /*+ gather_plan_statistics first_rows(10) */ *
from employees
where department_id = 50;
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 | | 1 | | 45 |00:00:00.01 | 9 |
| 1 | TABLE ACCESS BY INDEX ROWID| EMPLOYEES | 1 | 10 | 45 |00:00:00.01 | 9 |
|* 2 | INDEX RANGE SCAN | EMP_DEPARTMENT_IX | 1 | | 45 |00:00:00.01 | 4 |
-----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("DEPARTMENT_ID"=50
■ optimizer에게 영향을 주는 요소
- 오라클 버전
- 옵티마이저 관련 파라미터
- 통계정보
- 옵티마이저 힌트
- SQL과 연산자
- INDEX 설계
- 제약조건
SQL문 > PASER > Query Transformer > Estimator > plan Generator
1. Query Transformer : SQL을 최적화 하기 쉬운 형태로 변환
2. Estimator
- selectivily : 전체 대상 중 행에 특정한 조건에 의해 선택될것으로 예상되는 row 비율 (E-rows)
(1/num_distinct)
- cardinality : 특정 액세스 단계를 거치고나서 출력될 것으로 예상되는 결과 건수
(총 로우수 * 선택도(selectivily) = num_rows/num_distinct)
- cost : 특정 명령문을 실행하는데 필요한 표준화된 I/O에 대한 옵티마이저의 최적 예측 비용
- _optimizer_cost_model = {I/O|CPU|choose}
- I/O 비용 (8i) : 싱글io와 멀티io를 같은 선상에 둠
- CPU 비용 (10g)
- 단, 시스템 통계가 있으면 cpu 비용, 없으면 i/o 비용(9i)
3. Plan Generator : 후보군이 될만한 실행계획들을 생성하고, 그 중에서 수행비용이 가장 저렴한 계획을 리턴해준다.
HR@ora11g> drop table emp purge;
Table dropped.
HR@ora11g> create table emp as select * from employees;
Table created.
# 통계 수집
# method_opt=>'FOR ALL COLUMNS SIZE 1' : Histogram 생성하지 않겠다.
HR@ora11g> exec dbms_stats.gather_table_stats('HR','EMP',method_opt=>'FOR ALL COLUMNS SIZE 1')
PL/SQL procedure successfully completed.
HR@ora11g> select t.num_rows, c.column_name, c.num_nulls, c.num_distinct,
1/c.num_distinct selectivity, num_rows/c.num_distinct cardinality
from user_tables t, user_tab_columns c
where t.table_name = 'EMP'
and c.table_name = t.table_name;
NUM_ROWS COLUMN_NAME NUM_NULLS NUM_DISTINCT SELECTIVITY CARDINALITY
---------- -------------------- ---------- ------------ ----------- -----------
107 EMPLOYEE_ID 0 107 .009345794 1
107 FIRST_NAME 0 91 .010989011 1.17582418
107 LAST_NAME 0 102 .009803922 1.04901961
107 EMAIL 0 107 .009345794 1
107 PHONE_NUMBER 0 107 .009345794 1
107 HIRE_DATE 0 98 .010204082 1.09183673
107 JOB_ID 0 19 .052631579 5.63157895
107 SALARY 0 59 .016949153 1.81355932
107 COMMISSION_PCT 72 7 .142857143 15.2857143
107 MANAGER_ID 1 18 .055555556 5.94444444
107 DEPARTMENT_ID 1 11 .090909091 9.72727273
HR@ora11g> select job_id, count(*) from emp group by job_id;
JOB_ID COUNT(*)
---------- ----------
AC_MGR 1
AC_ACCOUNT 1
IT_PROG 5
ST_MAN 5
AD_ASST 1
PU_MAN 1
SH_CLERK 20
AD_VP 2
FI_ACCOUNT 5
MK_MAN 1
PR_REP 1
FI_MGR 1
PU_CLERK 5
SA_MAN 5
MK_REP 1
AD_PRES 1
SA_REP 30
HR_REP 1
ST_CLERK 20
# 플랜정보만 보기
HR@ora11g> explain plan for select * from emp where job_id = 'IT_PROG';
Explained.
HR@ora11g> select * from table(dbms_xplan.display);
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 6 | 414 | 3 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| EMP | 6 | 414 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------
ㄴ cardinality
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("JOB_ID"='IT_PROG')
> selectivity = 1/num_distinct = 1/19 = .052631579
> cardinality = num_rows * selectivity = 107 * .052631579 = 5.63157895 = 6
HR@ora11g> explain plan for select * from emp where job_id = 'SA_REP';
Explained.
HR@ora11g> select * from table(dbms_xplan.display);
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 6 | 414 | 3 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| EMP | 6 | 414 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("JOB_ID"='SA_REP')
> 컬럼별 그룹핑해야 값의 분포도 확인 가능 -> 전체 107건 job_id 칼럼의 고유값 19건
-> 값의 분포도가 고르지 않을 경우 -> 히스토그램을 생성해야 정확한 selectivity, cardinality 확인 가능하다.
■ Histogram
- 조건절에 자주 사용되는 컬럼들 중에 값의 분포도가 균일하지 않은 컬럼에 히스토그램을 생성한다.
- 히스토그램의 유형
- 도수분포히스토그램(Frequency) : 값의 수 = 버킷 개수(max 254)
- 높이균형히스토그램(Height-Balanced) : 값의 수 > 버킷 개수
- SIZE [integer | repeat | auto | skewonly]
- integer : 버킷개수를 설정 (1~254)
method_opt=>'FOR ALL COLUMNS SIZE 254' -- 모든 컬럼에 히스토그램 생성
method_opt=>'FOR COLUMNS SIZE 20 JOB_ID' -- JOB_ID 칼럼에 버킷 20개 만들고 히스토그램 생성
method_opt=>'FOR COLUMNS JOB_ID SIZE 20, DEPARTMENT_ID SIZE 12' -- 칼럼별 버킷 개수 설정
method_opt=>'FOR COLUMNS SIZE 20 JOB_ID, DEPARTMENT_ID' -- 여러 칼럼에 동일한 버킷 개수 설정
- repeat : 히스토그램이 기존에 생성되있던 컬럼에만 히스토그램 수집
method_opt=>'FOR ALL COLUMNS SIZE repeat'
- auto : 컬럼이 조건절에 사용되는 비중이 높은 컬럼(sys.col_usage$, col$)을 찾아 히스토그램 생성,
데이터 분포를 기준으로 자동결정
method_opt=>'FOR ALL COLUMNS SIZE auto'
- skewonly : 데이터 분포를 분석해 균일하지 않은 컬럼에 대해서 히스토그램 생성
# method_opt=>'FOR COLUMNS SIZE 20 JOB_ID'
HR@ora11g> exec dbms_stats.gather_table_stats('HR','EMP',method_opt=>'FOR COLUMNS SIZE 20 JOB_ID')
PL/SQL procedure successfully completed.
HR@ora11g> select column_name, num_distinct, num_buckets, histogram
from user_tab_col_statistics
where table_name = 'EMP';
COLUMN_NAME NUM_DISTINCT NUM_BUCKETS HISTOGRAM
-------------------- ------------ ----------- ---------------
EMPLOYEE_ID 107 1 NONE
FIRST_NAME 91 1 NONE
LAST_NAME 102 1 NONE
EMAIL 107 1 NONE
PHONE_NUMBER 107 1 NONE
HIRE_DATE 98 1 NONE
JOB_ID 19 19 FREQUENCY
SALARY 59 1 NONE
COMMISSION_PCT 7 1 NONE
MANAGER_ID 18 1 NONE
DEPARTMENT_ID 11 1 NONE
HR@ora11g> explain plan for select * from emp where job_id = 'IT_PROG';
Explained.
HR@ora11g> select * from table(dbms_xplan.display);
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 5 | 345 | 3 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| EMP | 5 | 345 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("JOB_ID"='IT_PROG')
HR@ora11g> explain plan for select * from emp where job_id = 'SA_REP';
Explained.
HR@ora11g> select * from table(dbms_xplan.display);
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 30 | 2070 | 3 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| EMP | 30 | 2070 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("JOB_ID"='SA_REP')
# 바인더 변수 사용 시
HR@ora11g> explain plan for select * from emp where job_id = :B1 ;
Explained.
HR@ora11g> select * from table(dbms_xplan.display);
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 6 | 414 | 3 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| EMP | 6 | 414 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("JOB_ID"=:B1)
> 바이너리 변수 처리 > 히스토그램을 무시한 결과값이 나온다.
> 값의 분포도가 좋지 않을 경우 변수처리는 안하는게 좋다.
> selectivity = 1/num_distinct
> cardinality = num_rows * selectivity
# job_id > :B1
HR@ora11g> explain plan for select * from emp where job_id > :B1 ;
Explained.
HR@ora11g> select * from table(dbms_xplan.display);
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 7 | 483 | 3 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| EMP | 7 | 483 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("JOB_ID">:B1)
> selectivity = 0.05(5%) 오라클이 도저히 selectivity 계산을 할 수 없을 경우 5% 사용
> cardinality = num_rows * selectivity
# salary > :B1
HR@ora11g> explain plan for select * from emp where salary > :B1 ;
Explained.
HR@ora11g> select * from table(dbms_xplan.display);
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 5 | 345 | 3 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| EMP | 5 | 345 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("SALARY">TO_NUMBER(:B1))
> 히스토그램이 생성되지 않으면 오라클이 알아서 계산
# salary between :B1 and :B2
HR@ora11g> explain plan for select * from emp where salary between :B1 and :B2 ;
Explained.
HR@ora11g> select * from table(dbms_xplan.display);
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 3 | 207 | 3 (0)| 00:00:01 |
|* 1 | FILTER | | | | | |
|* 2 | TABLE ACCESS FULL| EMP | 3 | 207 | 3 (0)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(TO_NUMBER(:B2)>=TO_NUMBER(:B1))
2 - filter("SALARY">=TO_NUMBER(:B1) AND "SALARY"<=TO_NUMBER(:B2))
> 범위스캔
> selectivity = 0.025 히스토그램이 생성되지 않으면 오라클이 알아서 계산
> cardinality = num_rows * selectivity
# 비중이 높은 컬럼찾기
HR@ora11g> exec dbms_stats.gather_table_stats('HR','EMP',method_opt=>'FOR COLUMNS SIZE 20 JOB_ID')
HR@ora11g> select object_name,data_object_id from user_objects where object_name = 'EMP';
OBJECT_NAME DATA_OBJECT_ID
-------------------- --------------
EMP 88896
SYS@ora11g> select a.*, b.name
from sys.col_usage$ a, col$ b
where a.obj# = b.obj#
and a.intcol# = b.col#
and a.obj# = 88896;
OBJ# INTCOL# EQUALITY_PREDS EQUIJOIN_PREDS NONEQUIJOIN_PREDS RANGE_PREDS LIKE_PREDS NULL_PREDS TIMESTAMP NAME
---------- ---------- -------------- -------------- ----------------- ----------- ---------- ---------- --------- ----------
88896 7 3 0 0 1 0 0 22-FEB-24 JOB_ID
88896 8 0 0 0 1 0 0 22-FEB-24 SALARY
'Oracle 11g > Tuning' 카테고리의 다른 글
92 Partition (1) | 2024.02.26 |
---|---|
91 통계정보 (0) | 2024.02.23 |
89 Pushing Subquery, View Merging, Pushdown (0) | 2024.02.21 |
88 Hash Join, unnest, eliminate_join, SEMI JOIN, ANTI JOIN (0) | 2024.02.20 |
87 Nested Loop Join, Sort Merge Join (0) | 2024.02.19 |