NL조인, 소트 머지 조인, 해시 조인에 대해 알아보자!
드라이빙 테이블(Outer 테이블)에서 읽은 결과를 Inner 테이블로 건건이 조인 시도for(i=0; i<I_MAX;i++){
for(j=0; j<J_MAX;j++){
...
}
}
-- ordered : from절에 나열된 순서대로 테이블 읽음
-- use_nl(테이블명) : nl조인을 사용
select /*+ gather_plan_statistics ordered use_nl(e) */ *
from dept d, emp e
where 1=1
and d.deptno=e.deptno;

dept 테이블에서 데이터를 한 건 읽어서 emp 테이블로 조인을 시도하고, 이것을 계속 반복
select /*+ ordered use_nl(b) */ a.*, b.*
from item a, uitem b
where a.item_id=b.item_id
and a.item_type_cd='100100'
and a.sale_yn='Y'
and b.sale_yn='Y';
-- 인덱스 : ITEM_TYPE_CD (item_x01)
-- 인덱스 : ITEM_ID+UITEM_ID (uitem pk)
ordered 힌트로 인해 item 테이블을 먼저 읽는다.item_x01을 이용해 a.item_type_cd='100100'의 데이터를 추출한다.최종 드라이빙 데이터로 만든다.위의 경우는 item_type_cd+sale_yn 인덱스와, item_id+sale_yn으로 인덱스를 구성하는 것이 더 이득일 수 있다.
중간에 멈추는 경우 이득조건에 맞게 먼저 읽은 후, 두 테이블을 조인 컬럼을 기준으로 정렬 후 조인 수행PGA에서 수행하는데 PGA는 프로세스에 할당된 독립된 공간이므로 버퍼 캐시(SGA)를 사용하는 NL조인에 비해 더 빠름select /*+ gather_plan_statistics ordered use_merge(e) */ *
from dept d, emp e
where 1=1
and d.deptno=e.deptno;

먼저 dept 테이블을 조인 컬럼 기준으로 정렬한다.
emp 테이블을 그 후 조인 컬럼 기준으로 정렬한다.
그 다음 두 테이블을 조인한다.
이 때, 첫번째 조인 시도 후 두번째부터는 첫 번째 시도한 만큼의 데이터는 조인 시도를 하지 않는다.
랜덤 액세스 부하 없음select /*+ ordered use_merge(b) */ a.*, b.*
from item a, uitem b
where a.item_id=b.item_id
and a.item_type_cd='100100'
and a.sale_yn='Y'
and b.sale_yn='Y';
-- 인덱스 : item_type_cd (item_x01)
ordered 힌트에 의해 item 테이블을 먼저 읽는다.조인 컬럼인 a.item_id를 기준으로 정렬테이블 전체를 읽으며 b.sale_yn='Y'인 데이터를 최종 추출 데이터로 만든다.정렬위의 경우 item_type_cd+sale_yn+item_id 인덱스와 sale_yn+item_id 로 인덱스를 구성하는 것이 이득이다.
랜덤 액세스를 줄일 수 있을 뿐더러 정렬을 대신할 인덱스가 있으면 정렬 부하를 줄일 수 있다.
해시 맵을 만들 때 두 테이블 중 작은 테이블을 읽음해시 버킷을 찾아가 실제 데이터를 찾음Build InputProbe Input조인 컬럼과 SELECT절에서 사용한 컬럼 까지 포함select /*+ gather_plan_statistics ordered use_hash(e) */ *
from dept d, emp e
where 1=1
and d.deptno=e.deptno;

dept 테이블을 먼저 읽어 Build Input으로 선택해 해시 맵을 만들고 조인을 시도한다.
Build Input을 선택하기 위해선 SWAP_JOIN_INPUTS(테이블명) 힌트를 사용해야 함select /*+ gather_plan_statistics ordered use_hash(e) swap_join_inputs(e) */ *
from dept d, emp e
where 1=1
and d.deptno=e.deptno;

Build Input이 커지면, PGA내 해시 영역 안에 적재가 힘들어 결국 디스크 공간을 사용하게 되므로 해시 조인의 성능이 떨어지게 된다.Outer 테이블에 정렬을 대신할 인덱스/(Group by/Order by 등의) 인라인 뷰가 있다면 정렬 시도 XInner 테이블의 크기가 작다면 정렬에 대한 부담 줄어듦등치(=) 조건으로 가능조인 컬럼 값에 중복 값이 적거나 where절에 조인 컬럼으로 값을 걸러내는 필터 조건이 없어야 함select a.*, b.*
from item a, uitem b
where a.item_id=b.item_id
and a.item_type_cd='100100'
and a.sale_yn='Y'
and b.sale_yn='Y';
--조인 방향 : item->uitem
NL 조인
item_type_cd+sale_yn 또는 그 반대로 인덱스 구성테이블 방문이 사라진다면, 인덱스에 추가테이블 방문한다면 인덱스에 조인 컬럼 추가 XNL조인의 핵심인 INNER 테이블에 조인 컬럼이 반드시 인덱스로 존재해야 함item_id+sale_yn 또는 반대로 인덱스를 구성소트 머지 조인
item_type_cd+sale_yn 또는 그 반대로 구성item_id를 위의 인덱스에 추가로 구성해 item_type_cd+sale_yn+item_id로 인덱스를 구성sale_yn+item_id로 만들어야 함 (그 반대로 구성시 인덱스 풀 스캔이 발생하게 됨)해시 조인
item_type_cd+sale_yn 또는 그 반대로 인덱스 생성sale_yn만으로 구성조인 컬럼이 인덱스에 포함되지 않아도 성능에 문제 없음NL조인의 특성상 Outer 조인 시, 조인 방향이 한쪽으로 고정Outer 기호(+)가 붙지 않은 테이블을 항상 드라이빙 테이블select /*+ gather_plan_statistics leading(d) use_nl(e) */ *
from dept d, emp e
where d.deptno=e.deptno(+);

조인 방향이 한 쪽으로 고정select /*+ gather_plan_statistics leading(d) use_merge(e) */ *
from dept d, emp e
where d.deptno=e.deptno(+);

조인 방향을 바꿀 수 있음해시 맵을 만들 수 있게 됨select /*+ gather_plan_statistics leading(d) use_hash(d e) */ *
from dept d, emp e
where d.deptno=e.deptno(+);

select /*+ gather_plan_statistics leading(d) use_hash(d e) swap_join_inputs(e) */ *
from dept d, emp e
where d.deptno=e.deptno(+);

스칼라 서브 쿼리 : 입력값에 대해 한 개의 반환 값만 반환캐시에 값을 저장해 둠입력 값과 출력 값을 저장해두고 서브 쿼리 실행 시, 캐시에서 입력값을 찾아 만약 있으면 미리 저장된 출력값 반환, 없으면 서브쿼리 실행해 결과 캐시에 저장자주 반복되고 값의 종류가 다양하지 않은 테이블과 조인시 사용하면 효과 좋음select /*+ gather_plan_statistics */ e.empno, e.ename, e.job,
(select dname from dept where deptno=e.deptno) dname,
(select loc from dept where deptno=e.deptno) loc
from emp e;
같은 테이블을 두 번 액세스하는 비효율이 발생하는데, 이럴 때 아래와 같이 SQL문을 수정
select empno, ename, job, regexp_substr(sub,'[^$]+',1,1) dname,
regexp_substr(sub,'[^$]+',1,2) loc
from (
select e.empno, e.ename, e.job,
(select dname || '$' || loc from dept where deptno=e.deptno) sub
from emp e);
--또는 TRIM 이용