{section}{column:width=300}
옵티마이져는 가능한 모든 수식의 값을 미리 구한다.
상수의 연산을 실행될 때마다 이루어지는 것이 아니라 *질의 변환단계에서 한 번만 수행*한다.
예)
① sales_qty > 1200/12
② sales_qty > 100
③ sales_qty*12 > 1200
=> ①,② 은 동일하게 취급이된다. 왜냐하면, 사전에 최대한의 연산을 하여 두 번째 조건식이 된다.
=> ③은 연산을 하여 ①,② 조건식을 만들지 않는다. 왜) 비교연산자의 추가적 연산은 연산자를 좌우로 이동시켜
단순화를 하지는 않기 때문이다.
단순화 시키는 문장
① job like 'SALESMAN'
② job = 'SALESMAN'
=> LIKE를 사용한 조건절에서 '%' 나 '_'를 사용하지 않는 경우에는 '=' 연산으로 *조건절로 단순화* 시킨다.
=> 가변길이 데이터타입일 때만 이러한 수식의 단순화가 가능하다.
조건절로 확장,변환시킨 문장
- 조건식에 IN, OR로 이용한 문장
① job IN ('CLERK','MANAGER')
② job = 'CLERK' OR job = 'MANAGER'
=> IN 비교 연산자를 사용한 문장에서는 OR 논리 연산자를 이용해 여러개의 '='로 된 같은기능의 조건절로 확장시킨다.
- 조건식에 ANY, SOME 로 이용한 문장
① sales_qty > ANY (:in_qty1, :in_qty2)
② sales_qty > :in_qty1 OR sales_qty > :in_qty2
=> 인반적인 조건식이라면 '=' 비교연산자와 OR논리 연산자를 이용해 같은 기능의 조건절로 확장시킬수있다.
- 조건식에 ALL 로 이용한 문장
① sales_qty > ALL (:in_qty1, :in_qty2)
② sales_qty > :in_qty1 AND sales_qty > :in_qty2
=> 조건절은 '=' 연산자와 AND 논리 연산자를 사용해 같은 기능의 조건절로 변환 시킨다.
- 조건식에 BETWEEN 를 이용한 문장
① sales_qty BETWEEN 100 and 200
② sales_qty >= 100 AND sales_qty <= 200
=> 비교연산자를 사용한 조건식은 '>=', '<=' 비교 연산자를 사용하여 변환된다.
- 조건식에 NOT를 이용한 문장
① NOT (sal < 30000 or comm is null)
② NOT sal < 30000 and comm is NOT null)
③ sal >= 30000 and comm is NOT null
=> 논리 연산자를 제거할 수 있는 반대 비교연산을 찾아 대체시키는 변환을 한다.
=> ① → ② → ③ 식으로 변환을 한다.
조건절에 서브쿼리사용 한 경우
- 조건식에 ANY, SOME 로 이용한 문장
① where 100000 > ANY (select sal from emp where job = 'CLERK')
② where EXISTS (select sal from emp where job = 'CLERK' and 100000 > sal)
=> EXISTS 연산자를 이용한 서브쿼리로 변환될 수 있다.
- 조건식에 ALL 로 이용한 문장
① where 100000 > ALL (select sal from emp where job = 'CLERK')
② where NOT (100000 <= ALL (select sal from emp where job = 'CLERK'))
③ where NOT EXISTS (select sal from emp where job = 'CLERK' and 100000 <= sal)
=> NOT ANY로 변환한 후에 다시 EXISTS 연산자를 이용하여 서브쿼리로 변환 될수있다.
- 조건식에 NOT를 이용한 문장
① NOT deptno = ( select deptno from emp where empno = 7689)
② deptno <> (select deptno from emp where empno = 7689)
=> NOT를 없애기 위해 반대 비교연산을 찾아 대체시키는 변환을 한다.
{column}{section}
{section}{column:width=300}
조건절에 같은 컬럼을 사용한 두 개의 조건식이 있다면 옵티마이져는 새로운 조건식을 이행성 규칙에 따라 생성하고
이 조건식을 이용하여 최적화를 수행한다.
원래의 조건식에서 인덱스를 사용하지 못하게 되더라도 인덱스를 사용하는 실행게획이 나타날 수도 있다.
----------------------------------------------------------------------------------------------------
가정.
where COLUMN1 comparison_operators constant
and COLUMN1 = COLUMN2
※ comparison_operators : =, !=, ^=, <, <>, >, <=, >= 중의 하나
constant : 연산, SQL함수, 문자열, 바인ㄷ 변수, 상관관계 변수를 포함하는 상수 수식
=> 3단 논법 A=B, B=C ,A=C 라는 원칙이 적용하듯이 옵티마이져도 같은 조건절을 추론한다.
COLUMN2 comparison_operators constant -----> 이것은 비용기준 옵티마이져일때만 사용가능하다.
- WHERE 절에 OR로 연결된 두개의 조건절이 있을 때 발생할 수 있는 경우
-----------------------------------------------------------------------------------------------------
※ 이행성규칙은 효율적이라고 판된될 때만 변환이 일어난다.
■ OR를 사용한 조건절을 분기시켰을 때 각각이 인덱스 접근 경로로 이용할 수 있다면 옵티마이져는 변환을 한다.
■ UNION ALL에 의해 각각의 인덱스를 경유하는 실행계획을 선택하고 나중에 결합을 한다.
■ 실행계획에서는 'IN-LIST ITERATOR' 나 'CONCATENATION'이라는 표시가 나타난다.
■ 조건절에 인덱스를 사용할수 없고 전체 테이블을 스캔한다고하면 OR를 사용한 조건절은 그 문장에 대한 변환을 하지않는다.
■ 변환을 못할경우 강제적으로 하는 힌트 'USE_CONCAT'사용한다.
select * from emp e, dept d where e.deptno = 20 and e.deptno = d.deptno;
=> 이행성규칙을 적용하여 d.deptno = 20을 추론할수있다
- 이행성 규칙이 일어나지 않는경우
예) where COLUMN1 comparion_operators COLUMN3
and COLUMN1 = COLUMN2
=> 비교하는 대상이 상수 수식이 아닌 컬럼인 경우는 이행이 일어나지 않는다.
=> COLUMN1 comparion_operators COLUMN3 DMS CNFHSGKWL DKSGSMSEK.
- OR를 가지는 조건들을 UNION ALL을 사용한 복합 문장으로 변환하는경우
select * from emp where job = 'CLERK' OR deptno = 10 ;
=> JOB과 DEPTNO 컬럼에 각각 인덱스가 존재한다면 옵티마이져는 위의 쿼리를 아래와 같은 쿼리로 변환한다.
select * from emp where job = 'CLERK'
UNION ALL
SELECT * from emp where deptno = 10 and job <> 'CLERK';
=> 이러한 현상은 우리가 IN을 사용한 상수수식 비교에서도 유사하게 나타난다. 그 이유는 이러한 형태의 IN은 OR로 변환되기 때문이다
※ 서브쿼리를 가진 복잡한 문장을 조인문장으로 변환하는경우 최적화하기 위해서 두가지 방법이있다.
① 이들을 같은 기능의 조인 문장으로 변환하여 다시이를 최적화하는 방법
② 현재의 문장을 그대로 최적화하는방법
SELECT * FROM emp WHERE deptno IN (SELECT deptno FROM dept WHERE loc = 'NEW YORK');
=> 만약 DEPT 테이브의 DEPTNO컬럼이 기본키나 유일 인덱스였다면 옵티마이져는==>
SELECT emp.* FROM emp, dept WHERE emp.deptno = dept.deptno AND dept.loc = 'NEW YORK';
조인문으로 변환할 수 없다면, 메인쿼리와 서브쿼리에 대한 실행 계획을 선택==>
SELECT * FROM emp WHERE sal >(SELECT AVG(sal) FROM emp WHERE depton = 20);
{column}{section}
뷰병합 - 뷰쿼리 : 우리가 뷰를 생성할 때 사용한 SELECT 문, 딕셔너리에 저장되어 있는 SELECT 문,FROM 절에 괄호로 묶어 둔 SELECT
- 뷰쿼리를 액세스 쿼리로 병합시키기
- 액세스 쿼리에 있는 뷰의 이름을 뷰쿼리의 원래의 테이블로 이름을 바꾸고, 뷰의 WHERE절에 있는 조건절을 액세스 쿼리 WHERE절에 추가 시킨다.
VIEW CREATE
create View emp_10 (e_no, e_name, job, manager, hire_date, salary, commission, deptno) AS
select empno, ename, job, mgr, hiredate, sal, comm, deptno
from emp
where deptno= 10;
- 뷰를 액세스하는 쿼리
select em_no, e_name, salary, hire_date
from emp_10
where salary >10000;
=> 액세스쿼리가 기준이 된다. 즉 액세스쿼리를 기준으로 뷰퀄에 있는 대응인자들을 병합한다.
- SQL 병합 완료 된 쿼리
select empno, ename, sal, hiredate
from emp
where deptnl = 10
and sal > 10000
=> 액세스쿼리에 있는 뷰를 원래의 테이블인 emp에 변환하고, 남아있는 조건절을 다시 액세스쿼리에 병합
- 액세스쿼리에있는 조건들을 뷰쿼리에 진입시킨경우
=> VIEW 생성
CREATE VIEW emp_union_view(e_no,e_name, job, mgr, hiredate,sal, comm,deptno)
AS select empno, ename, job, mgr, hiredate, sal, comm, deptno from regular_emp
UNION ALL
select empno, ename, job, magager, hiredate, salary, comm, 90 from temporary_emp;
① select e_no, e_name, mgr,sal
from emp_union_view
where deptnl = 20;
② select empno, ename, mgr, sal
from (select empno, ename, ggr, sal from regular_emp where deptno = 20
UNION ALL
select empno, ename, magager, salary from temporary_emp where 90 = 20);
=>① → ② 에 변환이 일어난다.
=> 변환된 쿼리를 보면 액세스쿼리에 있는 조건들이 각 select 문의 조건절속으로 파고 들어갔다.
=> 임시사원직은 부서코드를 가지지 않아서 '90' 을 지정했으므로 두번째 select의 조건절은 상수값을 서고비교하는모습니다.
- GROUP BY 를 사용한 뷰
create view emp_group_by_deptno
AS select deptno, avg(sal) avg_sal, mon(sal min_sal, max(sal) max_sal
from emp
GROUP BY deptno;
① select * from emp_group_by_deptno
where deptno = 10;
② select deptno, avg(sal) avg_sal, mon(sal min_sal, max(sal) max_sal
from emp
where deptno = 10
GROUP BY deptno;
=> ① → ② 에 변환이 일어난다.
=> 뷰는 조건절을 가지고 있지 않으면서 GROUP BY 를 하고 있으므로 마치 전체 테이블을 대상으로 GROUP BY 를 해 둔것처럼보인다.
=> 뷰 최적화를 위한 변환에 의해 나타나는 쿼리에는 액세스 쿼리에 부여한 조건들이 진입된다.
※ 뷰병합과 관련되 파라메터(complex_view_merging, optimizer_secure_view_merging) 가
작동상태되어 있으면 복잡한 뷰 내부에 GROUP BY 나 DISTINCT 를 사용했다라도 액세스쿼리의 조건들이 뷰쿼리에 파고들어갈수있다.
- 액세스 퀄리가 병합이 된경우
select d.loc, v.avg_sal
from dept d, emp_group_by_deptno v
wehre d.deptno = v.deptno
and d.loc = 'London';
병합된 쿼리 -> select dept.loc, avg(sal) 실행계획
from dept, emp SORT (GROUP BY)
where dept.deprtno = emp.deptno => NESTED LOOPS
and dept.loc = 'London' TABLE ACCESS (FULL) OF 'EMP'
GROUP BY dept.rowid, dept.loc; TABLE ACCESS (BY INDEX ROWID) OF 'DEPT'
INDEX (RANGE SCAN) OF
=> 원 뷰 쿼리에 GROUP BY DEPTO는 없어지고, 스스로 dept.rowid로 대체하였고
모든 범위의 액세스가 완료된 후에 SORT(GROUP BY)를 한다는 사실이다.
이 형태는 가능한 모든 조건이 먼저 적용된 후에 2차 가공을 하므로 가장 최적의 문장이 되었다.
- 힌트를 사용하여 액세스쿼리에 병합이 일어나지 안는경우
select /*+ NO_MERGE(v) */ 실행계획
d.loc, v.avg_sal HASH JOIN
from dept d, emp_group_by_deptno v => TABLE ACCESS (FULL) OF 'DEPT'
where d.deptno = v.deptno VIEW OF 'EMP_GROUP_BY_DEPTNO'
and d.loc = 'London'; SORT (GROUP BY)
TABLE ACCESS (FULL) OF 'EMP';
※ 병합이 불가능한 경우
■ 집합연산(UNION, UNION ALL, INTERSECT, MINUS)
■ CONNECT BY
■ ROWNUM 을 사용한경우
■ SELECT-List의 그룹함수(AVG,COUNT,MAX,MIN,SUM)
■ GROUP BY(단, Merge 힌트를 사용했거나 관련 파라메터가 Enable이면 뷰병합 가능)
■ SELECT-List 의 DISTINCT (단, Merge 힌트를 사용했거나 관련 파라메터가 Enable이면 뷰병합 가능)
- 인라인 뷰의 병합
select /*+ MERGE(V) */ e1.last_name, e1.sal, v.avg_salary
from emp e1, (select deptno, avg(sal) avg_salary
from emp e2
GROUP BY deptno ) v
where e1.deptno = v.deptno
and e1.sal > v.avg_salary;
실행계획 실행계획 분석
FILTER HASH JOIN
SORT (GROUP BY) TABLE ACCESS (FULL) OF 'EMP'
HASH JOIN => VIEW
TABLE ACCESS (FULL) OF 'EMP' SORT (GROUP BY)
TABLE ACCESS (FULL) OF 'EMP' TABLE ACCESS (FULL) OF 'EMP'
=> 먼저 인라인뷰를 수행하고 그결과 EMP테이블을 조인한다.
글므로 뷰병합이 안일어난게 유리하다.
※ 뷰병합의 적용에 관련된 파라메터는 기본값을 'TRUE'에 두는 것이 바람직하며, 위의 적용기준을 이용하여 필요하다면
NO_MERGE힌트를 적용하는 방법이 좋은 실행계획을 얻을 확률이 높다.
{column}{section}