튜닝교육06

Download Report

Transcript 튜닝교육06

오라클 데이터베이스
성능 튜닝
데이터 생성
인덱스
테이블
인덱스 데이터 값을 정렬하여 기록
들어오는 순서대로 기록
이름
ROWID
고객번호
이름
생일
A
page 7
109
D
90/01
C
page 3
108
K
90/01
D
page 1
102
C
92/05
D
page 4
103
D
92/06
F
page 5
104
F
93/01
K
page 8
105
K
page 2
101
A
93/10
page 6
100
K
93/10
2
93/10
테이블 액세스(인덱스 없는 경우)
Filter
WHERE
이름 = ‘K’
①
FULL TABLE SCAN
고객번호
이름
생일
100
D
90/01
X
101
K
90/01
O
102
C
92/05
X
103
D
92/06
X
104
F
93/01
X
105
X
93/10
X
106
A
93/10
X
107
K
93/10
O
3
테이블 액세스(인덱스 있는 경우)
Access
X
WHERE
이름 = ‘K’
INDEX SCAN
TABLE ACCESS BY ROWID
이름
A
ROWID
page 7
고객번호
이름
100
D
90/01
C
page 3
101
K
90/01
생일
D
page 1
102
C
92/05
D
page 4
103
D
92/06
F
page 5
104
F
93/01
K
page 8
105
X
93/10
K
page 2
106
A
93/10
X
page 6
107
K
93/10
4
인덱스 사용 기준
인덱스 대상 컬럼
•
조건문에 자주 등장하는 컬럼
•
같은 값이 적은 컬럼(분포도가 좁은 컬럼)
•
조인에 참여하는 컬럼
인덱스 사용시 손해보는 경우
•
데이터가 적은 테이블
•
같은 값이 많은 컬럼(분포도가 넓은 컬럼)
•
조회보다 DML의 부담이 큰 경우
5
결합 인덱스의 사용
결합 인덱스
2개이상의 컬럼이 조건문에 자주 등장시
X
이름
생일
A
93/10
C
ROWID
WHERE
AND
이름 = ‘C’
생일 > ‘92/12’
고객번호
이름
page 7
109
C
94/01
92/05
page 3
108
K
90/01
C
93/01
page 5
102
C
92/05
C
93/10
page 6
103
D
92/06
C
94/01
page 1
104
C
93/01
D
92/06
page 4
105
C
93/10
K
90/01
page 2
101
A
93/10
K
93/10
page 8
100
K
93/10
6
생일
결합 인덱스의 사용
SELECT 이름, 생일
FROM 고객테이블
결합 인덱스
인덱스만으로도 결과를 얻을 수 있을때 ... WHERE 이름 = ‘C’
AND 생일 > ‘92/12’
이름
X
생일
ROWID
고객번호
이름
생일
A
93/10
page 7
109
C
94/01
C
92/05
page 3
108
K
90/01
C
93/01
page 5
O
102
C
92/05
C
93/10
page 6
O
103
D
92/06
C
94/01
page 1
O
104
C
93/01
D
92/06
page 4
105
C
93/10
K
90/01
page 2
101
A
93/10
K
93/10
page 8
100
K
93/10
7
결합인덱스 사용 기준
결합인덱스를 사용하는 경우
•
자주 조건에 같이 등장하는 경우
•
인덱스만 읽고도 결과를 얻을 수 있을 때
결합인덱스의 컬럼 순서
•
사용빈도가 높은 컬럼부터
•
SCAN 범위를 줄여주는 컬럼부터
•
자주 사용되는 컬럼부터
8
스캔범위결정 조건과 검증조건
인덱스 범위결정
이름
X
WHERE
AND
AND
생일
ROWID
A
93/10
page 7
C
92/05
C
이름 = ‘C’
생일 > ‘92/12’
고객번호 = 105
고객번호
Filter
이름
생일
109
D
90/01
page 3
108
K
90/01
93/01
page 5
102
C
92/05
C
93/10
page 6
103
D
92/06
D
90/01
page 1
104
C
93/01
X
D
92/06
page 4
105
C
93/10
O
K
90/01
page 2
101
A
93/10
K
93/10
page 8
100
K
93/10
9
스캔범위결정 조건과 검증조건
Access 범위결정
이름 생일
X
WHERE
AND
AND
Filter
이름 = ‘C’
생일 like ‘%10’
고객번호 = 105
이름
Filter
ROWID
고객번호
생일
109
D
90/01
108
K
90/01
A
93/10
page 7
C
92/05
page 3
C
93/10
page 5
102
C
92/05
C
93/10
page 6
103
D
92/06
D
90/01
page 1
104
C
93/01
X
D
92/06
page 4
105
C
93/10
O
K
90/01
page 2
101
A
93/10
K
93/10
page 8
100
K
93/10
X
10
인덱스를 사용하지 못하는 경우
•
인덱스 컬럼에 변형이 일어난 경우
•
부정형으로 조건을 기술한 경우
•
NULL을 비교하였을 경우
•
내부적인 변형이 일어난 경우
•
옵티마이져의 판단에 따라 (cost-based optimizer)
11
인덱스 컬럼의 변형
Select …
from department
where max_salary * 12 > 2500;
부서번호
부서명
max_salary
max_salary
ROWID
100
xxxx.xxxx.xxxxxxxx
109
D
150
X
100
xxxx.xxxx.xxxxxxxx
108
K
180
X
150
xxxx.xxxx.xxxxxxxx
102
C
100
X
180
xxxx.xxxx.xxxxxxxx
103
D
200
X
200
xxxx.xxxx.xxxxxxxx
104
C
100
X
200
xxxx.xxxx.xxxxxxxx
105
C
350
O
300
xxxx.xxxx.xxxxxxxx
101
A
200
X
350
xxxx.xxxx.xxxxxxxx
100
K
300
O
12
인덱스 컬럼의 변형
Select …
from department
where max_salary > 2500/12 ;
부서번호
부서번호
ROWID
부서명
max_salary
100
xxxx.xxxx.xxxxxxxx
109
D
150
100
xxxx.xxxx.xxxxxxxx
108
K
180
150
xxxx.xxxx.xxxxxxxx
102
C
100
180
xxxx.xxxx.xxxxxxxx
103
D
200
200
xxxx.xxxx.xxxxxxxx
104
C
100
200
xxxx.xxxx.xxxxxxxx
105
C
350
300
xxxx.xxxx.xxxxxxxx
101
A
200
350
xxxx.xxxx.xxxxxxxx
100
K
300
13
O
O
부정형 조건
Select …
from Employee_TEMP
where 부서번호 <> ’100’ ;
부서번호
ROWID
99
xxxx.xxxx.xxxxxxxx
99
D
150
O
100
xxxx.xxxx.xxxxxxxx
100
K
180
X
100
xxxx.xxxx.xxxxxxxx
100
C
100
X
100
xxxx.xxxx.xxxxxxxx
100
D
200
X
100
xxxx.xxxx.xxxxxxxx
100
C
100
X
100
xxxx.xxxx.xxxxxxxx
100
C
350
X
100
xxxx.xxxx.xxxxxxxx
100
A
200
X
101
xxxx.xxxx.xxxxxxxx
101
K
300
O
부서번호
14
부서명
max_salary
부정형 조건
Select …
from Employee_TEMP
where 부서번호 < ‘100’ OR 부서번호 > ‘100’ ;
max_salary
ROWID
부서번호
부서명
max_salary
99
xxxx.xxxx.xxxxxxxx
99
D
150
100
xxxx.xxxx.xxxxxxxx
100
K
180
100
xxxx.xxxx.xxxxxxxx
100
C
100
100
xxxx.xxxx.xxxxxxxx
100
D
200
100
xxxx.xxxx.xxxxxxxx
100
C
100
100
xxxx.xxxx.xxxxxxxx
100
C
350
100
xxxx.xxxx.xxxxxxxx
100
A
200
101
xxxx.xxxx.xxxxxxxx
101
K
300
15
O
O
NULL 비교
Select …
Select …
from
from
Employee
Employee
where 생일 is not null;
where 생일 > ‘’;
Select …
Select …
from
from
Employee
where 급여 is not null;
Employee
where 급여 > 0 ;
16
내부변형
Select …
Select …
from
from
Employee
where 부서번호 = :A (상수)
where 부서번호 = to_char( :A)
Select …
Select …
from
Employee
from
Employee
Employee
where 생일 =
where 생일 = :B (일자)
to_char( :B, ‘yyyymmdd’)
17
SQL의 활용
1. EXISTS 대신 JOIN 의 사용
EXISTS 를 사용한 문장이 비효율적인 경우 JOIN 으로 변경하면 속도 향상을 볼 수 있음
EXISTS 사용 문장
JOIN 사용문장
SELECT …
FROM EMP E
WHERE EXISTS (SELECT 'X'
FROM DEPT D
WHERE D.DEPT_NO = E.DEPT_NO
AND D.DEPT_CAT = 'A');
SELECT ENAME ..
FROM DEPT D, EMP E
WHERE E.DEPT_NO = D.DEPT_NO
AND D.DEPT_CAT = 'A';
18
SQL의 활용
2. IN 대신 EXISTS 의 활용
ERD 상에서 base가 되는 테이블에 대한 query는 select할 때 여러 테이블과 join을
하는 경우가 많다. 이러한 경우 IN과 Sub-query를 사용하는 것 보다 EXISTS나
NOT EXISTS를 사용하는 것이 더 나은 성능을 보여주는 경우
IN 사용문장
EXISTS 사용문장
SELECT *
FROM EMP
WHERE EMPNO > 7777
AND DEPTNO IN (SELECT DEPTNO
FROM DEPT
WHERE LOC = 'MELB');
SELECT *
FROM EMP E
WHERE EMPNO > 7777
AND EXISTS (SELECT 'x'
FROM DEPT D
WHERE D.DEPTNO = E.DEPTNO
AND D.LOC = 'MELB');
19
SQL의 활용
3. NOT IN 대신 NOT EXISTS 의 활용
아래와 같이 Sub-query문에서 NOT IN은 내부적으로 sort와 merge를 수반한다.
NOT IN을 사용하면 대체적으로 가장 효율이 나쁜데, 이는 sub-query select에
대상이 되는 테이블을 강제로 full table scan 하도록 하기 때문이다
 NOT EXISTS 나 OUT JOIN 문장으로 변경
NOT IN 사용문장
SELECT …
FROM EMP
WHERE DEPT_NO NOT IN (SELECT DEPT_NO
FROM DEPT
WHERE DEPT_CAT = 'A');
NOT EXISTS 사용문장
OUTER JOIN 사용문장
SELECT …
FROM EMP E
WHERE NOT EXISTS (SELECT 'X'
FROM DEPT D
WHERE D.DEPT_NO = E.DEPT_NO
AND D.DEPT_CAT = 'A');
20
SELECT …
FROM EMP A, DEPT B
WHERE
A.DEPT_NO = B.DEPT_NO (+)
AND B.DEPT_NO IS NULL
AND B.DEPT_CAT(+) = 'A';
SQL의 활용
4. DISTINCT 대신 EXISTS 의 활용
유일성을 확보하기 위한 DISTINCT의 사용을 방지하기 위해서 아래와 같이
1:M 관계에서의 select에서는 EXISTS를 사용해야 한다
DISTINCT 사용문장
SELECT DISTINCT DEPT_NO, DEPT_NAME
FROM DEPT D, EMP E
WHERE D.DEPT_NO = E.DEPT_NO;
EMP 테이블 FULL SCAN
EXISTS 사용문장
SELECT DEPT_NO, DEPT_NAME
FROM DEPT D
WHERE EXISTS (SELECT 'X'
FROM EMP E
WHERE E.DEPT_NO = D.DEPT_NO);
EMP 테이블 INDEX SCAN
21
SQL의 활용
5. Row수 count 하기
일반적인 믿음과 달리 COUNT(*)가 COUNT(1)보다 빠르다.
만약 인덱스를 통해 COUNT 한 값을 추출하고자 할 때에는 인덱스로 잡혀있는 컬럼을
COUNT(EMPNO)와 같이 추출하는 것이 가장 빠르게 결과값을 얻을 수 있다.
6. Table Alias의 사용
여러 개의 테이블에 대한 Query시 항상 테이블에 대한 alias를 사용하고, 각각의 컬럼에 alias를
붙여 사용하는 것이 좋다.
ORACLE이 dictionary에서 해당 컬럼이 어느 테이블에 있는지를 찾지 않아도 되므로 parsing 시
간을 줄일 수 있고, 컬럼에 대한 혼동을 미연에 방지 할 수 있다.
22
SQL의 활용
7. WHERE절과 HAVING절의 차이
자주 사용하지는 않지만 간혹 HAVING을 WHERE 대신 사용하는 경우가 있다.
그러나 SELECT문에서 HAVING을 WHERE 대신 사용하는 것은 피하는 것이 좋다.
HAVING은 fetch된 row들에 대한 filter 역할을 한다. 여기에는 sort나 sum 등의 작업이 수반된다.
만약 select 하고자 하는 데이터를 일정 조건에 따라 추출하고자 할 경우에는 where절을 사용하
여 HAVING을 사용함으로써 발생할 수 있는 overhead를 줄여주는 것이 좋다.
HAVING절 사용문장
SELECT JOB, AVG(SAL)
FROM EMP
GROUP BY JOB
HAVING JOB = ‘PRESIDENT’
OR JOB = ‘MANAGER’;
GROUP BY 이후 결과를 FILTER 함
WHERE절 사용문장
SELECT JOB, AVG(SAL)
FROM EMP
WHERE JOB = ‘PRESIDENT’
OR JOB = ‘MANAGER’
GROUP BY JOB;
GROUP BY 전 데이터 ACCESS 단계에서 조건에서 FILER 함
23
SQL의 활용
8. SELECT절에서 Asterisk('*') 사용
Dynamic SQL 컬럼 '*'는 테이블의 모든 컬럼을 참조할 수 있게 해 준다. 그러나 이러한 '*'는 값
을 되돌려 줄 때 테이블의 모든 컬럼을 변환하여 반환하므로 매우 비효율적이다. SQL Parser는
Data Dictionary에서 해당 테이블에 대한 모든 컬럼의 이름을 읽어서 SQL 명령문 내의 '*'을 대체
하는 작업을 한다.비록 0.01초 밖에 더 걸리지 않는 작업일 지라도 여러 번 반복하면 많은 시간이
걸릴 수도 있으므로 되도록 Asterisk(*)를 사용하지 않는 것이 좋다.
9. UNION-ALL의 사용
두개의 query에 대해서 UNION을 사용할 때, 각각의 query에 의한 결과값이 UNION-ALL에 의해
합쳐지고 다시 내부 작업인 SORT UNIQUE 작업에 의해 최종 결과값을 사용자에게 되돌려 준다.
이 때 UNION 대신 UNION-ALL을 사용하게 되면 SORT UNIQUE 작업은 불필요하게 되며, 그만
큼의 시간을 줄일 수 있고 수행 성능을 향상시킬 수 있다.
이는 SORT가 필요하지 않은 경우에만 가능하므로 정확히 확인하고 사용하여야 한다.
24
SQL의 활용
10. ORDER BY를 사용하지 않고 정렬하기
ORDER BY을 사용할 때 인덱스를 사용하여 sort를 하지 않고 정렬된 결과값을
얻고자 할 때에는 다음의 두 조건을 만족하여야 한다.
▶ ORDER BY에 사용된 모든 컬럼이 동일한 순서로 하나의 인덱스로 만들어져 있어야 한다.
▶ ORDER BY에 사용된 모든 컬럼은 테이블 정의에 반드시 NOT NULL이어야 한다.
(Null값은 인덱스에 저장되지 않으므로)
Case 2에서처럼 의미 없는 Where에 의해 ORDER BY와 같이 sort 과정을 거치지 않고도 동일한
결과를 얻을 수 있다.
25
SQL의 활용
11. WHERE절에서 인덱스 사용을 위한 주의할 사항
아래처럼 몇몇 경우 Where절에 사용하는 비교문에 의해 인덱스를 사용할 수 없는 경우가 있다
26
SQL의 활용
12. DECODE 함수를 통한 내부처리 단축 ( DB CALL 줄이기 )
DECODE 함수를 활용하여 동일한 row에 대해 다시 읽는 것을 방지하거나
동일한 테이블에 대한 join을 피할 수 있다.
비슷한 문장을 두번 나누어 처리
SELECT COUNT(*), SUM(SAL)
FROM EMP
WHERE DEPT_NO = 0020
AND ENAME LIKE 'SMITH%';
SELECT COUNT(*), SUM(SAL)
FROM EMP
WHERE DEPT_NO = 0030
AND ENAME LIKE 'SMITH%';
DECODE 문으로 문장 통합
SELECT
COUNT(DECODE(DEPT_NO, 0020, 1, NULL)) D20_CNT,
SUM (DECODE(DEPT_NO, 0020, SAL, NULL)) D20_SAL,
COUNT(DECODE(DEPT_NO, 0030, 1, NULL)) D30_CNT,
SUM (DECODE(DEPT_NO, 0030, SAL, NULL)) D30_SAL
FROM EMP
WHERE ENAME LIKE 'SMITH%';
27
SQL의 활용
13. 1 QUERY 시 테이블에 대한 참조를 최소화 ( WHERE 조건 )
Query시 테이블에 대한 access 회수를 최소화 함으로써 성능을 향상 시킬 수 있다.
특히 Sub-query를 포함하거나 여러 컬럼에 대한 update를 수행할 때 작업 시간을
단축할 수 있다.
동일 테이블 두번참조 ( 비효율 )
SELECT TAB_NAME
FROM TABLES
WHERE TAB_NAME = (SELECT TAB_NAME
FROM TAB_COLUMNS
WHERE VERSION = 604)
AND DB_VER = (SELECT DB_VER
FROM TAB_COLUMNS
WHERE VERSION = 604);
테이블을 한번만 참조하도록 ( 효율적 )
SELECT TAB_NAME
FROM TABLES
WHERE (TAB_NAME, DB_VER) = (SELECT TAB_NAME, DB_VER
FROM TAB_COLUMNS
WHERE VERSION = 604);
28
SQL의 활용
13.2 QUERY 시 테이블에 대한 참조를 최소화 (MULTI COLUMN UPDATE )
UPDATE 컬럼이 하나 이상인 경우 효율적인 처리방법
동일 테이블 두번참조 ( 비효율 )
UPDATE EMP
SET EMP_CAT = (SELECT MAX(CATEGORY)
FROM EMP_CATEGORIES),
SAL_RANGE = (SELECT MAX(SAL_RANGE)
FROM EMP_CATEGORIES )
WHERE EMP_DEPT = 0020;
테이블을 한번만 참조하도록 ( 효율적 )
UPDATE EMP
SET (EMP_CAT, SAL_RANGE) = (SELECT MAX(CATEGORY), MAX(SAL_RANGE)
FROM EMP_CATEGORIES)
WHERE EMP_DEPT = 0020;
29
SQL의 활용
14. Stored Function을 활용한 SQL의 부하 감소
JOIN 수행후 GROUP BY 시 부하가 클 경우 부하를 감소 시킨 사례
JOIN 후 GROUP BY 수행한 경우
SELECT H.EMPNO, E.ENAME,
T.TYPE_DESC, COUNT(*)
FROM HISTORY_TYPE T, EMP E
WHERE H.EMPNO = E.EMPNO
GROUP BY H.EMPNO, E.ENAME, T.TYPE_DESC;
JOIN 후 GROUP BY 수행시 JOIN ,GROUP BY 수행에 부하 유발
GROUP BY 수행후 STORED FUNCTION 호출
SELECT H.EMPNO, Lookup_Emp(H.EMPNO),
H.HIST_TYPE, COUNT(*)
FROM EMP_HISTORY H
GROUP BY H.EMPNO, H.HIST_TYPE;
GROUP BY 만 수행후 최종 SELECT 절에서 FUNCTION 호출
30
CREATE OR REPLACE
FUNCTION Lookup_Emp
(emp IN NUMBER)
RETURN VARCHAR2
AS
ename VARCHAR2(30);
CURSOR C1 IS
SELECT ENAME
FROM EMP
WHERE EMPNO = emp;
BEGIN
OPEN C1;
FETCH C1 INTO ename;
CLOSE C1;
RETURN (NVL(ename, '?');
END;
Case 별 사례
MAX, MIN 사용 시 인덱스와 ROWNUM을 최대한 활용하라.
Select /*+index_desc(e I_sal ) */ sal
from employee e
where sal > 0 and ROWNUM =1;
Select max(sal) from employee
INDEX(SAL)
TABLE
●
●
●
●
S
O
R
T
31
Case 별 사례
select a.rmp into :b0
from mrdrm a
where a.c_cd='003'
and a.cno=:b1
and a.shcd=:b2
and a.dt= ( select max(b.dt)
from mrdrm b
where b.c_cd=a.c_cd
and b.cno= a.cno
and b.shcd=a.shcd
and b.dt<=:b3)
MRDRM_IDX02
EXECUTION PLAN
-------------------------------------------------------------------0
SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'MRDRM'
2 1
INDEX (RANGE SCAN) OF 'MRDRM_IDX02' (UNIQUE)
3 2
SORT (AGGREGATE)
4 3
INDEX (RANGE SCAN) OF 'MRDRM_IDX02' (UNIQUE)
인덱스 정보
---------------------------------------------------MRDRM_IDX02 : C_CD+CNO+SHCD+DT
MRDRM
'MRDRM_IDX02'
1:M
SORT후 MAX(DT)와 비교
SORT후 MAX(DT)와 비교
32
Case 별 사례
EXECUTION PLAN
select a.rmp into :b0
-----------------------------------------------------------------------0
SELECT STATEMENT Optimizer=CHOOSE
from mrdrm a
1
0
TABLE ACCESS (BY INDEX ROWID) OF 'MRDRM'
where a.c_cd='003'
2 1
INDEX (RANGE SCAN) OF 'MRDRM_IDX02' (UNIQUE)
and a.cno=:b1
3 2
COUNT (STOPKEY)
and a.shcd=:b2
4 3
INDEX (RANGE SCAN DESCENDING) OF 'MRDRM_IDX02'
and a.dt= ( select /*+index_desc
인덱스 정보
(b mrdrm_idx02)*/ b.dt
---------------------------------------------------from mrdrm b
MRDRM_IDX02 : C_CD+CNO+SHCD+DT
where b.c_cd=a.c_cd
and b.cno= a.cno
and b.shcd=a.shcd
and b.dt<=:b3
and ROWNUM=1)
'MRDRM_IDX02'
MRDRM
'MRDRM_IDX02'
1:1
역순으로 첫번째 DT와 비교
역순으로 첫번째 DT와 비교
33
Case 별 사례
SELECT 'OUT' IN_OUT,
'OWP' CLSS,
nvl(B.ITM_TYP_CD,' ') ITM_TYP_CD,
nvl(B.TRD_TR_DVCD,' ') TRD_TR_DVCD
FROM ITEM B
WHERE B.C_CD = :C_Cd
AND B.ORDDAY = DECODE(B.ORD_SVCD,'02',:svcDay,:WorkDay)
AND B.ITM_TYP_CD = '01’
AND B.ORD_SVCD IN ('01','02','04')
EXECUTION PLAN
--------------------------------------------------0
SELECT STATEMENT Optimizer=CHOOSE
1 0 PARTITION (CONCATENATED)
2 1
TABLE ACCESS (FULL) OF ‘ITEM'
34
Case 별 사례
SELECT 'OUT' IN_OUT,
'OWP' CLSS,
nvl(B.ITM_TYP_CD,' ') ITM_TYP_CD,
nvl(B.TRD_TR_DVCD,' ') TRD_TR_DVCD
FROM ITEM B
WHERE B.C_CD = :C_Cd
((B.ORDDAY = :TrDay
AND B.ORD_DVCD='02’) OR
(B.ORDDAY = :WorkDay AND B.ORD_DVCD<>'02’))
AND B.ITM_TYP_CD = '01’
AND B.ORD_SVCD IN ('01','02','04')
EXECUTION PLAN
------------------------------------------------------------------0
SELECT STATEMENT Optimizer=CHOOSE
1 0 CONCATENATION
2 1
TABLE ACCESS (BY GLOBAL INDEX ROWID) OF ‘ITEM'
3 2
INDEX (RANGE SCAN) OF 'PK_ITEM_IND01' (UNIQUE)
4 1
TABLE ACCESS (BY GLOBAL INDEX ROWID) OF ‘ITEM'
5 4
INDEX (RANGE SCAN) OF 'PK_ITEM_IND01' (UNIQUE)
35
Case 별 사례
1. SELECT A.C_CD,SUBSTR(A.AC_NO,10,2),
2.
sum(decode(B.AC_STUS_CD,'04',A.gen_de,0))
3.
FROM ACCOUNT B, CUSTOMER A
4. WHERE A.c_cd = :c_cd
5.
AND A.PD_CD LIKE :IN_pd_cd
6.
AND A.PD_CD <> '03'
7.
AND A.GEN
> 0
8.
AND A.C_CD
= B.C_CD
9.
AND A.AC_NO
= B.AC_NO
10.
AND B.AC_TYP_CD <> '06’
11.
AND SUBSTR(A.AC_NO,1,3) = :IN_org_cd
12.
AND SUBSTR(A.AC_NO,4,6) < '600000’
13. GROUP BY A.C_CD,SUBSTR(A.AC_NO,10,2),SUBSTR(A.AC_NO,1,3)
EXECUTION PLAN
---------------------------------------------------------------0
SELECT STATEMENT Optimizer=CHOOSE
1 0 SORT (GROUP BY)
2 1
PARTITION (CONCATENATED)
3 2
NESTED LOOPS
4 3
TABLE ACCESS (FULL) OF ‘CUSTOMER'
5 3
TABLE ACCESS (BY LOCAL INDEX ROWID) OF 'ACCOUNT'
6 5
INDEX (UNIQUE SCAN) OF 'PK_ACCCOUNT_01' (UNIQUE)
36
Case 별 사례
SELECT A.C_CD,
……
FROM ACCOUNT B,CUSTOMER A
WHERE A.c_cd = :c_cd
AND A.PD_CD LIKE :IN_pd_cd
AND A.PD_CD <> '03'
AND A.GEN
> 0
AND A.C_CD
= B.C_CD
AND A.AC_NO
= B.AC_NO
AND B.AC_TYP_CD <> '06'
AND A.AC_NO between :IN_org_cd||'00000000’
and :IN_org_cd||'59999999'
GROUP BY A.C_CD,SUBSTR(A.AC_NO,10,2),
SUBSTR(A.AC_NO,1,3)
EXECUTION PLAN
---------------------------------------------------------------0
SELECT STATEMENT Optimizer=CHOOSE
1 0 SORT (GROUP BY)
2 1
NESTED LOOPS
3 2
TABLE ACCESS (BY GLOBAL INDEX ROWID) OF ‘CUSTOMER'
4 3
INDEX (RANGE SCAN) OF 'PK_CUSTOMER' (UNIQUE)
5 2
PARTITION (SINGLE)
6 5
TABLE ACCESS (BY LOCAL INDEX ROWID) OF 'ACCOUNT'
7 6
INDEX (UNIQUE SCAN) OF 'PK_ACCCOUNT_01' (UNIQUE)
37