[SQLP] 쿼리 변환2
1. 복합 View merging
조건절과 조인문만을 포함하는 단순 뷰는 항상 뷰 머징이 일어난다.
뷰 내에 group by 절이 사용되거나(집약하는 역할 등) 뷰 내에 select 절에 distinct 연산자를 사용한 경우,
뷰는 시스템 설정에 따라 뷰 머징이 일어나거나 일어나지 않는다.
※_complex_view_metging 파라미터의 기본 값이 true 이면 북합 뷰 머징이 일어난다.
※ merge 힌트 사용 시 복합 뷰 머징이 일어난다.
예)
create view emp_sum as select deptno, sum(sal) as sum
from emp group by deptno;
explain plan for
select d.deptno, e.sum
from emp_sum e, dept d
where e.deptno = d.deptno
and d.loc = 'DALLAS';
select * from table(dbms_xplan.display);
Plan hash value: 860764689
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 37 | 7 (29)| 00:00:01 |
| 1 | MERGE JOIN | | 1 | 37 | 7 (29)| 00:00:01 |
|* 2 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | 11 | 2 (0)| 00:00:01 |
| 3 | INDEX FULL SCAN | PK_DEPT | 4 | | 1 (0)| 00:00:01 |
|* 4 | SORT JOIN | | 3 | 78 | 5 (40)| 00:00:01 |
| 5 | VIEW | EMP_SUM | 3 | 78 | 4 (25)| 00:00:01 |
| 6 | HASH GROUP BY | | 3 | 21 | 4 (25)| 00:00:01 |
| 7 | TABLE ACCESS FULL | EMP | 14 | 98 | 3 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
##뷰머징이 안일어나고 최적화를 한다.
##group by 의 힘인가?
##merge 힌트 사용하기
create view emp_sum3 as
select /*+merge*/deptno, sum(sal) as sum
from emp group by deptno;
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 3 | 81 | 7 (29)| 00:00:01 |
| 1 | HASH GROUP BY | | 3 | 81 | 7 (29)| 00:00:01 |
| 2 | MERGE JOIN | | 5 | 135 | 6 (17)| 00:00:01 |
|* 3 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | 20 | 2 (0)| 00:00:01 |
| 4 | INDEX FULL SCAN | PK_DEPT | 4 | | 1 (0)| 00:00:01 |
|* 5 | SORT JOIN | | 14 | 98 | 4 (25)| 00:00:01 |
| 6 | TABLE ACCESS FULL | EMP | 14 | 98 | 3 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
2. 복합 View merging이 일어나지 않는 경우
- 집합 연산자 사용
- connect by 절
- rownum 가상 컬럼 사용
- select 에 집계함수의 사용
- 윈도우 함수 사용
예)
##기존 실행계획
create view cust_test as select /*+merge*/sum(s.quantity_sold)total,s.cust_id, s.prod_id
from sales s
group by s.cust_id, s.prod_id;
explain plan for
select c.cust_id, c.cust_first_name, c.cust_last_name, c.cust_email
from customers c, products p, cust_test t
where c.cust_id = t.cust_id
and t.total > 100
and t.prod_id = p.prod_id
and p.prod_name = 'External 6X CD-ROM';
------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 648 | 68688 | 831 (1)| 00:00:10 | | |
|* 1 | FILTER | | | | | | | |
| 2 | HASH GROUP BY | | 648 | 68688 | 831 (1)| 00:00:10 | | |
|* 3 | HASH JOIN | | 12941 | 1339K| 830 (1)| 00:00:10 | | |
| 4 | NESTED LOOPS | | | | | | | |
| 5 | NESTED LOOPS | | 12941 | 682K| 422 (0)| 00:00:06 | | |
|* 6 | TABLE ACCESS FULL | PRODUCTS | 1 | 42 | 3 (0)| 00:00:01 | | |
| 7 | PARTITION RANGE ALL | | | | | | 1 | 28 |
| 8 | BITMAP CONVERSION TO ROWIDS | | | | | | | |
|* 9 | BITMAP INDEX SINGLE VALUE | SALES_PROD_BIX | | | | | 1 | 28 |
| 10 | TABLE ACCESS BY LOCAL INDEX ROWID| SALES | 12762 | 149K| 422 (0)| 00:00:06 | 1 | 1 |
| 11 | TABLE ACCESS FULL | CUSTOMERS | 55500 | 2818K| 407 (1)| 00:00:05 | | |
------------------------------------------------------------------------------------------------------------------------
nownum 을 추가하여 view을 생성한다면?
create view cust_test as select /*+merge*/sum(s.quantity_sold)total,s.cust_id, s.prod_id
from sales s
where rownum<4
group by s.cust_id, s.prod_id;
explain plan for
select c.cust_id, c.cust_first_name, c.cust_last_name, c.cust_email
from customers c, products p, cust_test t
where c.cust_id = t.cust_id
and t.total > 100
and t.prod_id = p.prod_id
and p.prod_name = 'External 6X CD-ROM';
-----------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time | Pstart| Pstop |
-----------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 92 | | 3084 (3)| 00:00:38 | | |
| 1 | NESTED LOOPS | | | | | | | | |
| 2 | NESTED LOOPS | | 1 | 92 | | 3084 (3)| 00:00:38 | | |
| 3 | NESTED LOOPS | | 1 | 52 | | 3083 (3)| 00:00:37 | | |
|* 4 | VIEW | CUST_TEST | 3 | 66 | | 3080 (3)| 00:00:37 | | |
| 5 | HASH GROUP BY | | 3 | 36 | 21M| 3080 (3)| 00:00:37 | | |
|* 6 | COUNT STOPKEY | | | | | | | | |
| 7 | PARTITION RANGE ALL | | 918K| 10M| | 498 (4)| 00:00:06 | 1 | 28 |
| 8 | TABLE ACCESS FULL | SALES | 918K| 10M| | 498 (4)| 00:00:06 | 1 | 28 |
|* 9 | TABLE ACCESS BY INDEX ROWID| PRODUCTS | 1 | 30 | | 1 (0)| 00:00:01 | | |
|* 10 | INDEX UNIQUE SCAN | PRODUCTS_PK | 1 | | | 0 (0)| 00:00:01 | | |
|* 11 | INDEX UNIQUE SCAN | CUSTOMERS_PK | 1 | | | 0 (0)| 00:00:01 | | |
| 12 | TABLE ACCESS BY INDEX ROWID | CUSTOMERS | 1 | 40 | | 1 (0)| 00:00:01 | | |
-----------------------------------------------------------------------------------------------------------------------
view 가 사용된 것을 볼 수 있다.
-nownum으로 인한 view merging 이 사용이 안되고 있다.
3. 조건절 pushing
-조건절 pushdown : 뷰를 참조하는 쿼리블록의 조건절을 뷰 쿼리블록 안으로 pushing 하는 기능
-조건절 pullup : 쿼리 블럭안에 있는 조건들을 쿼리 블록 밖으로 꺼내온다
-조인조건 push down : NL조인 수행 중에 드라이빙 테이블에서 읽은 값을 inner 뷰 쿼리 블록 안으로 밀어 넣는 것을 의미한다.
push down 은 NL 조인을 전제로 하므로 성능이 더 나빠질 수 있는데, 이에 대한 힌트는
/*+push_pred*/ , /*+no_push_pred*/ 를 제공한다.
예)
조건절 push down 의 경우
create index idx_emp_deptno on emp(deptno);
explain plan for
select deptno, avg_sal
from (select /*+no_merge index(emp idx_emp_deptno)*/ deptno, avg(sal)avg_sal from emp group by deptno ) a
where deptno = 30;
select * from table(dbms_xplan.display)
------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 26 | 2 (0)| 00:00:01 |
| 1 | VIEW | | 1 | 26 | 2 (0)| 00:00:01 |
| 2 | SORT GROUP BY NOSORT | | 1 | 7 | 2 (0)| 00:00:01 |
| 3 | TABLE ACCESS BY INDEX ROWID| EMP | 5 | 35 | 2 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | IDX_EMP_DEPTNO | 5 | | 1 (0)| 00:00:01 |
------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("DEPTNO"=30)
위의 xplan을 보면 view 을 위로 하고 index scan 이 하위에 있는 것을 알 수 있다.
이는 view 안에서 index tange scan 이 일어났다고 볼 수 있으며, 부의 쿼리 블럭안에서 행해졌다고 볼 수 있다.
--> 노머징일 때(거의 노머징일때만 발생한다), 옵티마이저는 이처럼 사용할 경우 성능상은 좋을 수 있다.
조인조건 push down
select * from table(dbms_xplan.display)
explain plan for
select /*+no_merge(e) push_pred(e) index(e inx_emp_deptno)*/*
from (select empno, ename,deptno from emp)e, dept d
where e.deptno(+) = d.deptno
and d.loc = 'chicago';
Plan hash value: 1099394308
------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 4 | 212 | 5 (0)| 00:00:01 |
| 1 | NESTED LOOPS OUTER | | 4 | 212 | 5 (0)| 00:00:01 |
|* 2 | TABLE ACCESS FULL | DEPT | 1 | 20 | 3 (0)| 00:00:01 |
| 3 | VIEW PUSHED PREDICATE | | 1 | 33 | 2 (0)| 00:00:01 |
| 4 | TABLE ACCESS BY INDEX ROWID| EMP | 5 | 65 | 2 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | IDX_EMP_DEPTNO | 5 | | 1 (0)| 00:00:01 |
------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("D"."LOC"='chicago')
5 - access("DEPTNO"="D"."DEPTNO")
- DEPT 테이블을 읽고 (Id 2)
- 인덱스 스캔으로 EMP 테이블의 행을 찾고 (Id 5)
- 인덱스를 통해 실제 EMP 테이블의 행을 읽음 (Id 4)
- 조인 조건을 서브쿼리 수준에서 처리 (Id 3)
- DEPT와 EMP 데이터를 결합 (Id 1)
- 최종 결과 반환 (Id 0)
Id 3의 "VIEW PUSHED PREDICATE"는, 쿼리에서 dept.d 테이블의 LOC = 'chicago' 조건을 emp 테이블에 관련된 서브쿼리로 "밀어 넣은" 것
즉, 이 조건을 EMP 테이블을 조회하기 전에 미리 적용하여 데이터 양을 줄이려고 한 것일 수 도..