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 Input
Probe 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 이용