CREATE TABLE EMP
(EMPNO NUMBER(4) CONSTRAINT EMP_PK PRIMARY KEY ,
ENAME VARCHAR2(10),
JOB VARCHAR2(9),
MGR NUMBER(4),
HIREDATE DATE,
SAL NUMBER(7, 2),
COMM NUMBER(7, 2),
DEPTNO NUMBER(2));
INSERT INTO EMP VALUES(7369, 'SMITH', 'CLERK', 7902,TO_DATE('17-DEC-1980', 'DD-MON-YYYY'), 800, NULL, 20);
INSERT INTO EMP VALUES(7499, 'ALLEN', 'SALESMAN', 7698,TO_DATE('20-FEB-1981', 'DD-MON-YYYY'), 1600, 300, 20);
INSERT INTO EMP VALUES(7521, 'WARD', 'SALESMAN', 7698,TO_DATE('22-FEB-1981', 'DD-MON-YYYY'), 1250, 500, 30);
INSERT INTO EMP VALUES(7566, 'JONES', 'MANAGER', 7839,TO_DATE('2-APR-1981', 'DD-MON-YYYY'), 2975, NULL, 20);
INSERT INTO EMP VALUES(7654, 'MARTIN', 'SALESMAN', 7698,TO_DATE('28-SEP-1981', 'DD-MON-YYYY'), 1250, 1400, 30);
INSERT INTO EMP VALUES(7698, 'BLAKE', 'MANAGER', 7839,TO_DATE('1-MAY-1981', 'DD-MON-YYYY'), 2850, NULL, 30);
INSERT INTO EMP VALUES(7782, 'CLARK', 'MANAGER', 7839,TO_DATE('9-JUN-1981', 'DD-MON-YYYY'), 2450, NULL, 10);
INSERT INTO EMP VALUES(7788, 'SCOTT', 'ANALYST', 7566,TO_DATE('09-DEC-1982', 'DD-MON-YYYY'), 3000, NULL, 20);
INSERT INTO EMP VALUES(7839, 'KING', 'PRESIDENT', NULL,TO_DATE('17-NOV-1981', 'DD-MON-YYYY'), 5000, NULL, 10);
INSERT INTO EMP VALUES(7844, 'TURNER', 'SALESMAN', 7698,TO_DATE('8-SEP-1981', 'DD-MON-YYYY'), 1500, 0, 30);
INSERT INTO EMP VALUES(7876, 'ADAMS', 'CLERK', 7788,TO_DATE('12-JAN-1983', 'DD-MON-YYYY'), 1100, NULL, 20);
INSERT INTO EMP VALUES(7900, 'JAMES', 'CLERK', 7698,TO_DATE('3-DEC-1981', 'DD-MON-YYYY'), 950, NULL, 30);
INSERT INTO EMP VALUES(7902, 'FORD', 'ANALYST', 7566,TO_DATE('3-DEC-1981', 'DD-MON-YYYY'), 3000, NULL, 20);
INSERT INTO EMP VALUES(7934, 'MILLER', 'CLERK', 7782,TO_DATE('23-JAN-1982', 'DD-MON-YYYY'), 1300, NULL, 10);
CREATE TABLE DEPT
(DEPTNO NUMBER(2) CONSTRAINT DEPT_PK PRIMARY KEY,
DNAME VARCHAR2(14),
LOC VARCHAR2(13) );
INSERT INTO DEPT VALUES (10, 'ACCOUNTING', 'NEW YORK');
INSERT INTO DEPT VALUES (20, 'RESEARCH', 'DALLAS');
INSERT INTO DEPT VALUES (30, 'SALES', 'CHICAGO');
INSERT INTO DEPT VALUES (40, 'OPERATIONS', 'BOSTON');
CREATE TABLE SAL(
EMPNO NUMBER(4),
YYMMDD VARCHAR2(8),
SALARY NUMBER,
SUDANG NUMBER,
CONSTRAINT SAL_PK PRIMARY KEY (EMPNO,YYMMDD)
)
INSERT INTO SAL VALUES(7369,'20060101',1000,500);
INSERT INTO SAL VALUES(7369,'20060201',1000,500);
INSERT INTO SAL VALUES(7369,'20060301',1000,500);
INSERT INTO SAL VALUES(7369,'20060401',1000,500);
INSERT INTO SAL VALUES(7499,'20060101',1000,500);
INSERT INTO SAL VALUES(7499,'20060201',1000,500);
INSERT INTO SAL VALUES(7499,'20060301',1000,500);
INSERT INTO SAL VALUES(7902,'20060301',1000,500);
INSERT INTO SAL VALUES(7902,'20060401',1000,500);
INSERT INTO SAL VALUES(7934,'20060401',1000,500);
CREATE TABLE FAMILY(
EMPNO NUMBER(4),
SEQ NUMBER,
RELASHION VARCHAR2(5),
BUYANG CHAR(1),
CONSTRAINT FAMILY_PK PRIMARY KEY (EMPNO,SEQ)
)
INSERT INTO FAMILY VALUES(7369,1,'부','Y');
INSERT INTO FAMILY VALUES(7369,2,'모','Y');
INSERT INTO FAMILY VALUES(7369,3,'형','N');
INSERT INTO FAMILY VALUES(7369,4,'누나','N');
INSERT INTO FAMILY VALUES(7369,5,'동생','N');
INSERT INTO FAMILY VALUES(7499,1,'부','Y');
INSERT INTO FAMILY VALUES(7499,2,'형','Y');
INSERT INTO FAMILY VALUES(7902,1,'모','N');
INSERT INTO FAMILY VALUES(7902,2,'형','N');
SELECT
부서명
,B.사원번호
,AVG(SUBSTR(DISTINCT D.ROWID||급여총액,19,15))*COUNT(DISTINCT C.ROWID))*0.12
FROM 부서 A,사원 B,급여 C,가족 D
WHERE A.부서코드=:DEPT_CD
AND A.부서코드=B.부서코드
AND B.사원번호=C.사원번호( + )
AND B.사원번호=D.사원번호( + )
AND C.부양여부( + )='Y'
AND D.년월( + ) BETWEEN TO_CHAR(TO_DATE(:INDATE||'10','YYYYMMDD')-90,'YYYYMM') AND :INDATE
예제의 쿼리처림 1:M:M의 관계를 가지는 테이블들을 단순하게 조인시 1*M쪽 로우수*M쪽 로우수 만큼의 데이타 복제가 발생하여 옳바르지 못한 값이 출력된다
SELECT
부서명
,B.사원번호
,AVG_AMT*DECODE(B.직무,'A1',0.12,0.11)*가족수
FROM 부서 A,사원 B
,(
SELECT 사원번호,COUNT(*) 가족수
FROM 가족
WHERE 부양여부='Y'
GROUP BY 사원번호
) C
,(
SELECT 사원번호,AVG(급여총액) AVG_AMT
FROM 급여
WHERE 년월( + ) BETWEEN TO_CHAR(TO_DATE(:INDATE||'10','YYYYMMDD')-90,'YYYYMM') AND :INDATE
) D
WHERE A.부서코드=:DEPT_CD
AND A.부서코드=B.부서코드
AND B.사원번호=C.사원번호( + )
AND B.사원번호=D.사원번호( + )
M에 해당하는 가족,급여 테이블을 사원번호로 GROUP BY하여 사원테이블과 1:1의 관계를 가지게 함으로써 데이타 복제에 따른 오류가 발생하지 않는다.
SELECT
A.DNAME,B.EMPNO,CNT,AMT
FROM DEPT A,EMP B
,(
SELECT EMPNO,COUNT(*) CNT
FROM FAMILY
WHERE BUYANG='Y'
GROUP BY EMPNO
) C
,(
SELECT EMPNO,SUM(SALARY) AMT
FROM SAL
WHERE YYMMDD BETWEEN '20060101' AND '20060331'
GROUP BY EMPNO
) D
WHERE A.DEPTNO=20
AND A.DEPTNO=B.DEPTNO
AND B.EMPNO=C.EMPNO(+)
AND B.EMPNO=D.EMPNO(+)
Operation Object Name Rows Bytes Cost Object Node In/Out PStart PStop
SELECT STATEMENT Optimizer Mode=CHOOSE
MERGE JOIN OUTER
MERGE JOIN OUTER
SORT JOIN
MERGE JOIN
TABLE ACCESS BY INDEX ROWID SCOTT.DEPT
INDEX UNIQUE SCAN SCOTT.DEPT_PK
FILTER
TABLE ACCESS FULL SCOTT.EMP
SORT JOIN
VIEW
SORT GROUP BY
TABLE ACCESS FULL SCOTT.SAL
SORT JOIN
VIEW
SORT GROUP BY
TABLE ACCESS FULL SCOTT.FAMILY
SELECT
A.DNAME,B.EMPNO,CNT,AMT
FROM DEPT A,EMP B
,(
SELECT EMPNO,COUNT(*) CNT
FROM FAMILY
WHERE BUYANG='Y'
GROUP BY EMPNO
) C
,(
SELECT EMPNO,SUM(SALARY) AMT
FROM SAL
WHERE YYMMDD BETWEEN '20060101' AND '20060331'
GROUP BY EMPNO
) D
WHERE A.DEPTNO=20
AND B.EMPNO > 0 --추가
AND A.DEPTNO=B.DEPTNO
AND B.EMPNO=C.EMPNO(+)
AND B.EMPNO=D.EMPNO(+)
Operation Object Name Rows Bytes Cost Object Node In/Out PStart PStop
SELECT STATEMENT Optimizer Mode=CHOOSE 5 9
HASH JOIN OUTER 5 340 9
HASH JOIN OUTER 5 210 6
NESTED LOOPS 5 80 3
TABLE ACCESS BY INDEX ROWID SCOTT.DEPT 1 11 1
INDEX UNIQUE SCAN SCOTT.DEPT_PK 4
TABLE ACCESS BY INDEX ROWID SCOTT.EMP 5 25 2
INDEX RANGE SCAN SCOTT.EMP_PK 14 1
VIEW 3 78 2
SORT GROUP BY 3 12 2
TABLE ACCESS BY INDEX ROWID SCOTT.FAMILY 5 20 2
INDEX RANGE SCAN SCOTT.FAMILY_PK 9 1
VIEW 4 104 2
SORT GROUP BY 4 52 2
TABLE ACCESS BY INDEX ROWID SCOTT.SAL 9 117 2
INDEX RANGE SCAN SCOTT.SAL_PK 9 1
Operation Object Name Rows Bytes Cost Object Node In/Out PStart PStop
NESTED LOOPS
TABLE ACCESS (BY INDEX ROWID) OF 'TAB1'
INDEX (RANGE SCAN) OF 'INDEX_1'
VIEW
SORT (GROUP BY)
TABLE ACCESS (FULL) OF 'TAB2'
1) 처리절차:3-26 페이지 참조
2) NESTED LOOPS 조인의 나중에 위치한 집합이라고 해서 반드시 전체를 반복 수행하는 것은 아니다.
3) 즉 TAB1에서 10개의 로우가 추출되었을 경우 각 로우마다 TAB2에 대해 테이블 풀 스캔 후 GROUP BY를 하고 난 후 그 결과와 조인하는 것이 아니라
TAB2의 풀 스캔한 후 GROUP BY한 결과를 내부적으로 저장한 후 이 결과와 10개의 로우에 대해 조인을 한다.
4) 따라서 비록 TAB2가 테이블 풀 스캔이지만 테이블 크기가 크지 않거나 자체적인 조건에 의해 처리범위를 줄일 수 있다면 충분히 활용가능한 방법이다.
SELECT
MIN(부서명)
,X.사원번호
,AVG(급여총액)*MIN(DECODE(직무,'A1',0.12,0.11)*가족수
FROM
(
SELECT
B.사원번호
,MIN(부서명) 부서명
,MIN(B.직무) 직무
,COUNT(C.사원번호) 가족수
FROM 부서 A,사원 B,가족 C
WHERE B.부서코드=A.부서코드
AND C.사원번호 ( + )=B.사원번호
AND A.부서코드= : DEPT_CD
AND C.부양여부='Y'
GROUP BY B.사원번호
) X,급여 Y
WHERE Y.사원번호 ( + )=X.사원번호
AND Y.년월( + ) BETWEEN TO_CHAR(TO_DATE(:INDATE||'10','YYYYMMDD')-90,'YYYYMM') AND :INDATE
GROUP BY X.사원번호
- 강좌 URL : http://www.gurubee.net/lecture/2489
- 구루비 강좌는 개인의 학습용으로만 사용 할 수 있으며, 다른 웹 페이지에 게재할 경우에는 출처를 꼭 밝혀 주시면 고맙겠습니다.~^^
- 구루비 강좌는 서비스 제공을 위한 목적이나, 학원 홍보, 수익을 얻기 위한 용도로 사용 할 수 없습니다.