실행계획 SQL 연산(HASH SEMI-JOIN)
세미 조인은 보통 EXISTS를 사용하는 서브쿼리의 형태로 나타나며 이러한 경우 서브 쿼리에 인덱스가 존재하지 않는다면 상당히 비효율적인데 이러한 서브 쿼리에 인덱스가 없는 경우 SEMI-JOIN이 일어나도록 유도한다면 성능의 향상을 꽤할 수 있습니다. 즉 인덱스가 없는 EXISTS를 사용하는 쿼리라면 HASH_SJ or MERGE_SJ 힌트 구분을 이용해서 세미조인이 일어나도록 푸는 것이 좋습니다.
아래의 질의는 EMP 테이블에서 부서에 속해있는 직원들의 이름, 급여를 출력하는 예입니다.
(현시점에 DEPT TABLE의 DEPTNO는 인덱스가 존재한다고 가정하겠습니다)
SQL>SELECT ENAME, SAL
FROM EMP E
WHERE EXISTS ( SELECT 1
FROM DEPT D
WHERE E.DEPTNO = D.DEPTNO);
Execution Plan
--------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 FILTER
2 1 TABLE ACCESS (FULL) OF ‘EMP’
3 1 INDEX (RANGR SCAN) OF ‘idx_dept_deptno’ (NON-UNIQUE)
다음과 같이 인덱스를 제거한 후 다시 위의 쿼리를 실행해 보면…
SQL>drop index idx_dept_deptno;
SQL>SELECT ENAME, SAL
FROM EMP E
WHERE EXISTS ( SELECT 1
FROM DEPT D
WHERE E.DEPTNO = D.DEPTNO);
Execution Plan
--------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 FILTER
2 1 TABLE ACCESS (FULL) OF ‘EMP’
3 1 TABLE ACCESS (FULL) OF ‘DEPT’
위의 경우 서브쿼리의 조건 컬럼에 인덱스가 없으므로 EMP 테이블의 한 건에 대해 DEPT 테이블을 계속 FULL SCAN 해나가므로 성능이 떨어지게 됩니다. 이를 세미조인이 일어나도록 유도한 SQL문은 다음과 같습니다.
SQL>SELECT ENAME, SAL
FROM EMP E
WHERE EXISTS ( SELECT /*+ HASH_SJ */ 1
FROM DEPT D
WHERE E.DEPTNO = D.DEPTNO);
Execution Plan
--------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 SORT(AGGREGATE)
2 1 HASH JOIN (SEMI)
3 2 TABLE ACCESS (FULL) OF ‘EMP’
4 2 TABLE ACCESS (FULL) OF ‘DEPT’
2017년 4월 17일 월요일
(구로디지털단지,IT실무,오라클강좌,자바강좌,Oracle Hint) 실행계획 SQL 연산(HASH ANTI-JOIN)
실행계획 SQL 연산(HASH ANTI-JOIN)
ANTI 조인은 테이블의 레코드를 추출하는 경우 조인의 대상이 되는 테이블과 일치하지 않는 데이터를 추출하는 연산 입니다. SQL연산에서 NOT IN, NOT EXISTS, MINUS등이 해당되며 이러한 안티 조인은 MERGE ANTI-JOIN or HASH ANTI_JOIN으로 풀리도록 할 수 있습니다.
아래의 Query는 동일한 의미를 가지는 질의 입니다. 확인해 보세요~
SQL> SELECT EMPNO,
ENAME,
SAL
FROM EMP
WHERE (EMPNO, ENAME, SAL) NOT IN (SELECT EMPNO,
ENAME,
SAL
FROM EMP_OLD);
Execution Plan
--------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 FILTER
2 1 TABLE ACCESS (FULL) OF ‘EMP’
3 1 TABLE ACCESS (FULL) OF ‘EMP_OLD’
SQL> SELECT EMPNO,
ENAME,
SAL
FROM EMP E
WHERE NOT EXISTS (SELECT 1
FROM EMP_OLD EO
WHERE EO.EMPNO = E.EMPNO
AND EO.ENAME = E.ENAME
AND EO.SAL = E.SAL);
Execution Plan
-------------------------------------------------------------
SELECT STATEMENT Optimizer=CHOOSE
1 0 FILTER
2 1 TABLE ACCESS (FULL) OF ‘EMP’
3 1 TABLE ACCESS (FULL) OF ‘EMP_OLD’
SQL> SELECT EMPNO, ENAME, SAL FROM EMP
MINUS
SELECT EMPNO, ENAME, SAL FROM EMP_OLD
Execution Plan
-------------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 MINUS
2 1 SORT (UNIQUE)
3 2 TABLE ACCESS (FULL) OF ‘EMP’
4 1 SORT (UNIQUE)
5 2 TABLE ACCESS (FULL) OF ‘EMP_OLD’
위의 세 Query중 HASH ANTI JOIN으로 풀 수 있는 것은 NOT IN을 포함하고 있는 첫번째 질의 입니다. NOT IN의 비교 대상이 되는 컬럼은 NOT NULL로 서브쿼리까지 명시해 주어야 합니다. 물론 HASH_AJ 라는 힌트 구문도 사용해야 하구요~
SQL> SELECT EMPNO,
ENAME,
SAL
FROM EMP
WHERE EMPNO IS NOT NULL
AND ENAME IS NOT NULL
AND SAL IS NOT NULL
AND (EMPNO, ENAME, SAL)
NOT IN (SELECT /*+ HASH_AJ */
EMPNO,
ENAME,
SAL
FROM EMP_OLD
WHERE EMPNO IS NOT NULL
AND ENAME IS NOT NULL
AND SAL IS NOT NULL);
Execution Plan
--------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 HASH JOIN(ANTI)
2 1 TABLE ACCESS (FULL) OF ‘EMP’
3 1 TABLE ACCESS (FULL) OF ‘EMP_OLD’
HSH ANTI JOIN으로 풀 경우 성능이 향상되므로 위 문장과 같이 한 테이블에 존재하지 않는 로우만 추출하는 경우엔 HASH ANTI JOIN이 되도록 힌트를 사용하는 것이 유리합니다.
ANTI 조인은 테이블의 레코드를 추출하는 경우 조인의 대상이 되는 테이블과 일치하지 않는 데이터를 추출하는 연산 입니다. SQL연산에서 NOT IN, NOT EXISTS, MINUS등이 해당되며 이러한 안티 조인은 MERGE ANTI-JOIN or HASH ANTI_JOIN으로 풀리도록 할 수 있습니다.
아래의 Query는 동일한 의미를 가지는 질의 입니다. 확인해 보세요~
SQL> SELECT EMPNO,
ENAME,
SAL
FROM EMP
WHERE (EMPNO, ENAME, SAL) NOT IN (SELECT EMPNO,
ENAME,
SAL
FROM EMP_OLD);
Execution Plan
--------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 FILTER
2 1 TABLE ACCESS (FULL) OF ‘EMP’
3 1 TABLE ACCESS (FULL) OF ‘EMP_OLD’
SQL> SELECT EMPNO,
ENAME,
SAL
FROM EMP E
WHERE NOT EXISTS (SELECT 1
FROM EMP_OLD EO
WHERE EO.EMPNO = E.EMPNO
AND EO.ENAME = E.ENAME
AND EO.SAL = E.SAL);
Execution Plan
-------------------------------------------------------------
SELECT STATEMENT Optimizer=CHOOSE
1 0 FILTER
2 1 TABLE ACCESS (FULL) OF ‘EMP’
3 1 TABLE ACCESS (FULL) OF ‘EMP_OLD’
SQL> SELECT EMPNO, ENAME, SAL FROM EMP
MINUS
SELECT EMPNO, ENAME, SAL FROM EMP_OLD
Execution Plan
-------------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 MINUS
2 1 SORT (UNIQUE)
3 2 TABLE ACCESS (FULL) OF ‘EMP’
4 1 SORT (UNIQUE)
5 2 TABLE ACCESS (FULL) OF ‘EMP_OLD’
위의 세 Query중 HASH ANTI JOIN으로 풀 수 있는 것은 NOT IN을 포함하고 있는 첫번째 질의 입니다. NOT IN의 비교 대상이 되는 컬럼은 NOT NULL로 서브쿼리까지 명시해 주어야 합니다. 물론 HASH_AJ 라는 힌트 구문도 사용해야 하구요~
SQL> SELECT EMPNO,
ENAME,
SAL
FROM EMP
WHERE EMPNO IS NOT NULL
AND ENAME IS NOT NULL
AND SAL IS NOT NULL
AND (EMPNO, ENAME, SAL)
NOT IN (SELECT /*+ HASH_AJ */
EMPNO,
ENAME,
SAL
FROM EMP_OLD
WHERE EMPNO IS NOT NULL
AND ENAME IS NOT NULL
AND SAL IS NOT NULL);
Execution Plan
--------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 HASH JOIN(ANTI)
2 1 TABLE ACCESS (FULL) OF ‘EMP’
3 1 TABLE ACCESS (FULL) OF ‘EMP_OLD’
HSH ANTI JOIN으로 풀 경우 성능이 향상되므로 위 문장과 같이 한 테이블에 존재하지 않는 로우만 추출하는 경우엔 HASH ANTI JOIN이 되도록 힌트를 사용하는 것이 유리합니다.
피드 구독하기:
글 (Atom)