사례 1. 년 / 월이 분리된 경우에도 인덱스 사용

55
DB 기기기기기 1 SQL Tuning 기기 1. 기 / 기기 기기기 기기기기 기기기 기기 기기기기기 기기기기기 TAB_A : TAB_A_X1 : YY + MM YY + MM + DD + SALE_NO QUERY QUERY SELECT A.YY || A.MM, DEPT, SUM(SALE_QTY) FROM TAB_A A WHERE A.YY || A.MM BETWEEN '9410' AND '9504' A.YY || A.MM BETWEEN '9410' AND '9504' GROUP BY A.YY || A.MM, DEPT ; QUERY QUERY SELECT A.YY || A.MM, DEPT, SUM(SALE_QTY) FROM TAB_A A WHERE (A.YY = '94' AND A.MM BETWEEN '10' AND '12') (A.YY = '94' AND A.MM BETWEEN '10' AND '12') OR (A.YY = '95' AND A.MM BETWEEN '01' AND '04') OR (A.YY = '95' AND A.MM BETWEEN '01' AND '04') GROUP BY A.YY || A.MM, DEPT ;

Upload: dillian

Post on 13-Jan-2016

57 views

Category:

Documents


1 download

DESCRIPTION

사례 1. 년 / 월이 분리된 경우에도 인덱스 사용. 인덱스정보 TAB_A : TAB_A_X1 : YY + MM + DD + SALE_NO QUERY. SELECT A.YY || A.MM, DEPT, SUM(SALE_QTY) FROM TAB_A A WHERE A.YY || A.MM BETWEEN '9410' AND '9504' GROUP BY A.YY || A.MM, DEPT ;. 개선된 QUERY. SELECT A.YY || A.MM, DEPT, SUM(SALE_QTY) - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 1

SQLTuning

SQLTuning 사례 1. 년 / 월이 분리된 경우에도 인덱스 사용

인덱스정보인덱스정보TAB_A : TAB_A_X1 : YY + MMYY + MM + DD + SALE_NO

QUERYQUERYSELECT A.YY || A.MM, DEPT, SUM(SALE_QTY) FROM TAB_A A

WHERE A.YY || A.MM BETWEEN '9410' AND '9504'A.YY || A.MM BETWEEN '9410' AND '9504' GROUP BY A.YY || A.MM, DEPT ;

개 선 된 개 선 된 QUERYQUERY SELECT A.YY || A.MM, DEPT, SUM(SALE_QTY)

FROM TAB_A A

WHERE (A.YY = '94' AND A.MM BETWEEN '10' AND '12')(A.YY = '94' AND A.MM BETWEEN '10' AND '12')

OR (A.YY = '95' AND A.MM BETWEEN '01' AND '04')OR (A.YY = '95' AND A.MM BETWEEN '01' AND '04')

GROUP BY A.YY || A.MM, DEPT ;

Page 2: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 2

SQLTuning

SQLTuning

SELECT NVL (DEPTNO, ' ')

INTO :B1

FROM EMP

WHERE EMPNO = :B2 EMPNO = :B2 ;

사례 2. BINDING 변수의 인덱스 사용 확인

테이블 정보테이블 정보

인덱스 정보인덱스 정보 EMP : EMP_EMPNO_PK :

EMPNO

QUERYQUERY ROWS EXECUTION PLANROWS EXECUTION PLAN------ ----------------------------------------------

0 SELECT STATEMENTSELECT STATEMENT1314813148 TABLE ACCESS (TABLE ACCESS (BY ROWIDBY ROWID) OF 'EMP') OF 'EMP' 00 INDEX (INDEX (RANGE SCANRANGE SCAN) OF 'EMP_EMPNO_PK' ) OF 'EMP_EMPNO_PK' ((UNIQUEUNIQUE))

1) Dynamic SQL1) Dynamic SQL 에서 주의에서 주의2) HOST 2) HOST 변수 선언에서 주의변수 선언에서 주의

EMPEMP

# * EMPNO VARCHAR2(5) ENAME VARCHAR2(20) DEPTNO VARCHAR2(5)

( 총 로우 수 :13,148)

Page 3: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 3

SQLTuning

SQLTuning

SELECT H14_ACNT_CODE, H14_GWANLI_CODE, H14_MAGAM_YY, H14_MAGAM_MM, H14_GUBUN, H14_C_AMT, H14_D_AMT FROM FH14 WHERE H14_MAGAM_YY = :B0 AND H14_GUBUN = :B1

ORDER BY H14_ACNT_CODE, H14_GWANLI_CODEORDER BY H14_ACNT_CODE, H14_GWANLI_CODE ;

사례 3. ORDER BY 절에서도 인덱스 사용

인덱스정보인덱스정보

FH14 : IFH14_KEY1 : H14_ACNT_CODE + H14_GWANLI_CODEH14_ACNT_CODE + H14_GWANLI_CODE + H14_MAGAM_YY

+ H14_MAGAM_MM + H14_GUBUN QUERYQUERY

ROWS EXECUTION PLANROWS EXECUTION PLAN ---- ----------------------------------------------- 0 SELECT STATEMENT HINT: CHOOSE 5743 TABLE ACCESS (BY ROWID) OF 'FH14'168188 INDEX (RANGE SCAN) OF 'IFH14_KEY1' (UNIQUE)

Order By 에 의한SORT Plan 이 없는 이유 ?

전제 : 적어도 하나이상의 컬럼이 NOT NULL

Page 4: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 4

SQLTuning

SQLTuning

SELECT STOCK_Q FROM GFLM600 WHERE IO_DATEIO_DATE = (SELECT MAX(IO_DATE)MAX(IO_DATE) FROM GFLM600 WHERE CAR_CODE = :VALUE1 AND BOX_CODE = :VALUE2) ;

사례 4. MAX 값의 처리

인덱스정보인덱스정보GFLM600 : GFLM600_PK : CAR_CODE + BOX_CODE + IO_DATE

QUERYQUERY두 두 SUB-QuerySUB-Query 의 차이는 의 차이는 ??

SELECT STOCK_Q FROM GFLM600 A WHERE IO_DATE = (SELECT MAX(IO_DATE) FROM GFLM600 B WHERE B.CAR_CODE = :VALUE1 AND B.BOX_CODE = :VALUE2 AND A.CAR_CODE = B.CAR_CODE AND A.CAR_CODE = B.CAR_CODE AND A.BOX_CODE = B.BOX_CODE ) ;AND A.BOX_CODE = B.BOX_CODE ) ;

원하는 결과 나오지 않음

Page 5: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 5

SQLTuning

SQLTuning

SELECT /*+ INDEX_DESC(A GFLM600_PK) *//*+ INDEX_DESC(A GFLM600_PK) */ STOCK_Q FROM GFLM600 A WHERE CAR_CODE = :VALUE1 AND BOX_CODE = :VALUE2 AND ROWNUM = 1 ;AND ROWNUM = 1 ;

SELECT SUBSTR (MAX (IO_DATE || STOCK_Q), 9, 4)SUBSTR (MAX (IO_DATE || STOCK_Q), 9, 4) FROM GFLM600 WHERE CAR_CODE = :VALUE1 AND BOX_CODE = :VALUE2;

사례 4. MAX 값의 처리

해결 방안해결 방안 1) 1) 역순 인덱스 사용역순 인덱스 사용

2) SUBSTR 2) SUBSTR 사용 사용 (( 인덱스 없을 때인덱스 없을 때 ))

Page 6: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 6

SQLTuning

SQLTuning 사례 5. 불필요한 DUAL

인덱스정보인덱스정보PATN : PATN_PK : ID + DAT (PRIMARY KEY)PATN : PATN_IDX1 : DEPT + GUBUNPATN : PATN_IDX2 : WARD + GUBUN

QUERYQUERY

SELECT ID, DEPT, WARD, ROOM INTO :B1, :B2, :B3, :B4 FROM PATN WHERE WARD = :C1 AND NVL(DAT,'999999') >= TO_CHAR(SYSDATE, 'YYMMDD') TO_CHAR(SYSDATE, 'YYMMDD') ORDER BY DEPT, ROOM ;

SELECT ID, DEPT, WARD, ROOM INTO :B1, :B2, :B3, :B4 FROM PATN WHERE WARD = :C1 AND NVL(DAT, '999999') >= (SELECT TO_CHAR(SYSDATE, 'YYMMDD')SELECT TO_CHAR(SYSDATE, 'YYMMDD') FROM SYS.DUALFROM SYS.DUAL) ORDER BY DEPT, ROOM ;

개선된개선된 QUERYQUERY

Page 7: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 7

SQLTuning

SQLTuning

A 110 10

A 111 11

A 112A 112 5

COL1 COL2 ROWID

COL2 COL1 ROWID

INDEX ( COL1 + COL2 ) INDEX ( COL2 + COL1 )

결합 인덱스의 처리 범위 ( 둘 다 '=' 로 쓰인 경우 )

110 A 10

110 B 41

110 C 57

111 A 11

111 B 39

111 C 76

112 A112 A 5

112 B 73

112 C 89

113 A 18 114 A 22

A 115 23

A 113 18

A 114 22

A 116 29

A 117 25

A 118 26

B 110 41

B 111 39

SELECT * FROM TAB1 SELECT * FROM TAB1 WHERE COL1 WHERE COL1 = = 'AA' (( 분포도가 넓다분포도가 넓다 )) AND COL2 AND COL2 == '112112' (( 분포도가 좁다분포도가 좁다 ))

Page 8: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 8

SQLTuning

SQLTuning

A 110 10

A 111A 111 11

A 112A 112 5

A 115 23

A 113A 113 18

A 114 22

A 116 29

A 117 25

A 118 26

B 110 41

B 111 39

COL1 COL2 ROWID

COL2 COL1 ROWID

SELECT * FROM TAB1 SELECT * FROM TAB1 WHERE COL1 WHERE COL1 = = 'AA'AND COL2 AND COL2 BETWEENBETWEEN '111111' AND AND '113113'

INDEX ( COL1 + COL2 ) INDEX ( COL2 + COL1 )

결합 인덱스의 처리 범위 ( 둘 다 '=' 로 안 쓰인 경우 )

110 B 41

110 C 57

111 A111 A 11

111111 B 39

111 111 C 76

112 A112 A 5

112112 B 73

112 112 C 89

113 A113 A 18 113113 B 44

114 A 22

Page 9: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 9

SQLTuning

SQLTuning

SELECT ACT_CODE, PART_MODEL_CODE, PART_CHAR_CODE, PART_SPEC, PART_ROUT_CODE, ORDERNO, ITEMNO, DIN_ACT_MH,

WORK_ORDER_NO, S_MODEL_NO, DRAW_NO, MATR_STATUS, WORK_ORDER_DATE,

ROWIDFROM S_WORKPLANWHERE WORK_ORDER_DATE WORK_ORDER_DATE LIKELIKE :B1 || '%' :B1 || '%' AND DEPT_CODE = :B2AND DEPT_CODE = :B2 AND GROUP_CODE = :B3AND GROUP_CODE = :B3ORDER BY ACT_CODE ;

사례 6. 결합 인덱스의 컬럼 순서에 따른 차이

인덱스정보인덱스정보

S_WORKPLAN : S_WORKPLAN_IDX : WORK_ORDER_DATEWORK_ORDER_DATE + DEPT_CODE +

GROUP_CODE

QUERYQUERY

해결방안해결방안 : 인덱스 칼럼 순서 : DEPT_CODE + GROUP_CODE + DEPT_CODE + GROUP_CODE +

WORK_ORDER_DATEWORK_ORDER_DATE

ROWS EXECUTION PLANROWS EXECUTION PLAN----- ---------------------------------------------------- 0 SELECT STATEMENT 2220 SORT (ORDER BY) 2220 TABLE ACCESS (BY ROWID) OF 'S_WORKPLAN'4602646026 INDEX (RANGE SCAN) OF 'S_WORKPLAN_IDX' (NON UNIQ)

Page 10: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 10

SQLTuning

SQLTuning 사례 6. 결합 인덱스의 컬럼 순서에 따른 차이

WORK_ORDER_DATE + DEPT_CODE + GROUP_CODE LIKE '1'1%%'' = 1010 = 22

WORK_ORDER

11A 10 111A 10 2A 10 211A 20 311A 30 411B 10 2B 10 211B 20 211B 30 311B 40 411C 10 111C 10 2C 10 211C 20 32A 10 1

10 1 1A10 1 1C10 1 2A10 2 110 2 1A10 2 110 2 1B10 2 110 2 1C20 2 1B20 3 1A20 3 1C30 3 1B30 4 1A40 4 1B

DEPT_CODE + GROUP_CODE + WORK_ORDER_DATE = 1010 = 22 LIKE '1'1%%''

DEPT_CODE

GROUP_CODE

WORK_ORDER

DEPT_CODE

GROUP_CODE

Page 11: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 11

SQLTuning

SQLTuning

SELECT SHIPPER, COUNT(*), SUM(AMT) FROM EX_SHIPPER WHERE SDATE BETWEENSDATE BETWEEN '950101' AND '950430' <- 4 <- 4 개월 분량개월 분량 , , 약 약 33 33 만 건만 건 AND SHIPPER INSHIPPER IN ('A', 'B', 'C') GROUP BY SHIPPER ORDER BY SUM(AMT) DESC ;

사례 7. 간단한 결합 인덱스지만 컬럼 순서 유의

인덱스정보인덱스정보

EX_SHIPPER : SHIP_DATE_IX : SDATESDATE + SHIPPEREX_SHIPPER 테이블의 분포도 : SDATE : 1 / 365 , SHIPPER : 1 / 10

QUERYQUERY

EXECUTION PLANEXECUTION PLAN----------------------------------------------SELECT STATEMENT COST ESTIMATE:N/A SORT ( GROUP BY ) TABLE ACCESS BY ROWID EX_SHIPPER ( 1 ) INDEX RANGE SCAN SHIP_DATE_IX ( NU )

Page 12: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 12

SQLTuning

SQLTuning 사례 7. 간단한 결합 인덱스지만 컬럼 순서 유의

SHIP_DATE_IX

SDATE +SDATE +SHIPPERSHIPPER

EX_SHIPPER

22

차차

가가

공공

SHIP_DATE_IX

SHIPPER +SHIPPER +SDATESDATE

EX_SHIPPER

22

차차

가가

공공

Page 13: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 13

SQLTuning

SQLTuning 사례 7. 간단한 결합 인덱스지만 컬럼 순서 유의

SHIPPER IN (SHIPPER IN (‘‘CC’’, , ‘‘BB’’, , ‘‘AA’’))

SHIP_DATE_IX1 : SHIPPER + SDATE + SHIP_DATE_IX1 : SHIPPER + SDATE +

AMTAMT

SHIP_DATE_IX

2 2

차차

가가

공공

SHIPPER + SDATE + AMTSHIPPER + SDATE + AMT

●●

●●

●●

EXECUTION PLANEXECUTION PLAN---------------------------------------------------SELECT STATEMENT SORT ( GROUP BY ) CONCATENATION INDEX RANGE SCAN : SHIP_DATE_IX1 ( NU ) INDEX RANGE SCAN : SHIP_DATE_IX1 ( NU ) INDEX RANGE SCAN : SHIP_DATE_IX1 ( NU )

처리범위는'SHIPPER =' 와'SDATE BETWEEN ...'에 의해서 결정

Page 14: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 14

SQLTuning

SQLTuning

인덱스정보인덱스정보GFLT950 A : GFLT950_X1 : CAR_CODE + D_DAY + PART_CODEPART_CODE + TEAM_CODE (PK)GFLT950 A : GFLT950_X2 : TEAM_CODE + SPUM_CODEGFXC130 B : GFXC130_X1 : D_DAY + PART_CODE + TEAM_CODE (PK)

SELECT A.CAR_CODE, A.D_DAY, A.PART_CODE, A.SPUM_CODE,

A.ORDER_PRT, A.HP_Q, B.TEAM_CDOE, B.UPFR_DATE FROM GFLT950 A, GFXC130 B

WHERE A.PART_CODE = B.PART_CODEWHERE A.PART_CODE = B.PART_CODE AND A.ORDER_PRT = 100 AND B.D_DAY = :VALUE1 AND B.TEAM_CODE = :VALUE2 AND B.PART_CODE BETWEEN :VALUE3

AND :VALUE4;

사례 8. 조인시 사용된 결합 인덱스의 컬럼 순서

QUERYQUERY

ROWS EXECUTION PLANROWS EXECUTION PLAN----- -------------------------------------------------- 0 SELECT STATEMENT 184 NESTED LOOP1771817718 TABLE ACCESS (FULL) OF 'GFLT950' 184 TABLE ACCESS (BY ROWID) OF 'GFXC130' 184 INDEX (UNIQUE SCAN) OF 'GFXC130_X1' (UNIQUE)

AA BB

해결 방안 해결 방안 : : GFLT950 A : GFLT950_X1 : PART_CODEPART_CODE + CAR_CODE + D_DAY +

TEAM_CODE

Page 15: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 15

SQLTuning

SQLTuning

GFLT950

# * PART_CODE# * CAR_CODE# * D_DAY# * TEAM_CODE SPUM_CODE ORDER_PRT F_HP_Q

사례 8. 조인시 사용된 결합 인덱스의 컬럼 순서

GFXC130

# * PART_CODE

# * D_DAY

# * TEAM_CODE

B_F_QTY

UPPER_DATE

UP

인덱스정보인덱스정보GFLT950 A : GFLT950_X1 : CAR_CODE + D_DAY + PART_CODEPART_CODE +

TEAM_CODE (PK)GFLT950 A : GFLT950_X2 : TEAM_CODE + SPUM_CODEGFXC130 B : GFXC130_X1 : D_DAY + PART_CODE + TEAM_CODE (PK)

Page 16: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 16

SQLTuning

SQLTuning

SELECT A.CAR_CODE, A.D_DAY, A.PART_CODE, A.SPUM_CODE, A.ORDER.PRT, A.F_HP_Q, B.TEAM_CODE FROM GFLT920 A, GFXC130 B WHERE A.PART_CODE = A.PART_CODE = B.PART_CODE AND A.D_DAY = :VALUE1 AND B.PART_CODEB.PART_CODE BETWEENBETWEEN :VALUE3 AND :VALUE4 AND B.TEAM_CODE = :VALUE2;

사례 9. 결합 인덱스를 구성하는 컬럼 선택

인덱스정보인덱스정보 GFLT920 A : GFLT920_X1 : CAR_CODE + D_DAY + PART_CODE + TEAM_CODE

(PK) GFLT920 A : GFLT920_X2 : PART_CODEPART_CODE + SPUM_CODE GFXC130 B : GFXC130_X1 : PART_CODEPART_CODE + TEAM_CODE (PRIMARY KEY)

QUERYQUERY

ROWS EXECUTION PLANROWS EXECUTION PLAN---- ------------------------------------------------ 0 SELECT STATEMENT HINT : CHOOSE 23 NESTED LOOP 12 TABLE ACCESS (BY ROWID) OF 'GFXC130' 13 INDEX (RANGE SCAN) OF 'GFXC130_X1' (UNIQUE)6389 TABLE ACCESS (BY ROWID) OF 'GFLT920'63906390 INDEX (RANGE SCAN) OF 'GFLT920_X2'

BB

AA

Page 17: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 17

SQLTuning

SQLTuning 사례 9. 결합 인덱스를 구성하는 컬럼 선택

12121313 63896389 23231212

GFXC130테이블 B

GFLT920테이블 A

GFXC130_X1 GFLT920_X2

해결방안해결방안

GFLT920 A : GFLT920_X2 : PART_CODE + D_DAYPART_CODE + D_DAY

63906390

●●●

X

O

O

X

Page 18: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 18

SQLTuning

SQLTuning

SELECT A.PART_CODE, A.SPUM_CODE, A.D_DAY, A.B_F_QTY, A.TAG, B.ORDER_PRT, C.DC_CODE FROM GFBT400 A, GFXC440 BGFXC440 B, GFXC130 CGFXC130 C WHERE A.SPUM_CODE = B.SPUM_CODEB.SPUM_CODE AND A.PART_CODE = C.PART_CODEC.PART_CODE AND A.D_DAY = :B1A.D_DAY = :B1 AND A.TEAM_CODE = :B2AND A.TEAM_CODE = :B2;

사례 10. 결합 인덱스를 구성하는 컬럼 순서

인덱스정보인덱스정보GFBT400 A : GFBT400_X3 : TEAM_CODETEAM_CODE + SHOP_CODE + D_DAYD_DAY GFXC440 B : GFXC440_X1 : SPUM_CODESPUM_CODE (UNIQUE)GFXC130 C : GFXC130_X1 : PART_CODEPART_CODE (UNIQUE)

QUERYQUERY

ROWS EXECUTION PLANROWS EXECUTION PLAN----- --------------------------------------------------------- 0 SELECT STATEMENT 391 NESTED LOOPS 391 NESTED LOOPS 391391 TABLE ACCESS ( BY ROWID ) OF 'GFBT400'4827648276 INDEX ( RANGE SCAN ) OF 'GFBT400_X3' (NON-UNIQ) 391 TABLE ACCESS ( BY ROWID ) OF 'GFXC130' 391 INDEX ( UNIQUE SCAN ) OF 'GFXC130_X1' (UNIQ) 391 TABLE ACCESS ( BY ROWID ) OF 'GFXC440' 391 INDEX ( UNIQUE SCAN ) OF 'GFXC440_X1' (UNIQ)

AA

CC

BB

Page 19: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 19

SQLTuning

SQLTuning 사례 10. 결합 인덱스를 구성하는 컬럼 순서

GFBT400_X3

TEAM_CODETEAM_CODE+ SHOP_CODE+ D_DAY

4827648276

GFBT400 AA

GFXC130_X1

391

GFXC130 CC

PART_CODE

GFXC440 BB

391

GFXC440_X1

SPUM_CODE

391 391391391

해결방안 해결방안 : : TEAM_CODE + D_DAYTEAM_CODE + D_DAY + SHOP_CODE+ SHOP_CODE

O

OX

X

X

OXX

X

Page 20: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 20

SQLTuning

SQLTuning 사례 11. 조인하는 테이블의 순서에 따른 차이

수주진행현황 C# * 회사번호 # * 부서번호 # * 주문번호 # * 주문일자 # * 고객번호 직원번호

고객 D

# * 고객번호 고객이름 주소 주민등록번호 출고정지구분

주문 B

# * 주문번호 * 부서번호 * 고객번호 * 주문일자 * 직원번호 주문량

납품실행계획 A

# * 부서번호# * 주문번호 # * 직원번호 * 주문일자 납품예정일 소요비용 완료여부

Page 21: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 21

SQLTuning

SQLTuning 사례 11. 조인하는 테이블의 순서에 따른 차이

인덱스정보인덱스정보납품실행계획 A : 납품실행 _PK : 주문번호주문번호 ++ 부서번호부서번호 + 직원번호주문 B : 주문 _PK : 주문번호 주문번호 수주진행현황 C : 수주진행현황 _PK : 회사번호 회사번호 + + 부서번호부서번호 + 주문번호 + 주문일자 + 고객번호 고객 D : 고객 _PK : 고객번호고객번호SELECT DISTINCTDISTINCT A.부서번호 , A.주문번호 , B.주문일자 , B.직원번호 , B.

고객번호 , D.출고중지구분 FROM 납품실행계획 납품실행계획 AA , 주문 주문 B ,B , 수주진행현황 수주진행현황 C C , 고객 고객 DD WHERE A.A. 부서번호부서번호 = B.부서번호 AND A.A. 주문번호주문번호 = B.주문번호 AND A.주문일자 = B.주문일자 AND C.부서번호 = B.부서번호 AND C.고객번호 = B.고객번호 AND C.직원번호 = B.직원번호 AND C.주문번호 = B.B. 주문번호주문번호 AND C.주문일자 = B.주문일자 AND C.고객번호 = D.D. 고객번호고객번호 AND C.C. 부서번호 부서번호 = 3000= 3000 AND C. C. 회사번호 회사번호 = 3 = 3 AND A.완료여부 IS NULL ;

ROWS EXECUTION PLANROWS EXECUTION PLAN---- ------------------------------------------------------------- 0 SELECT STATEMENT 7 SORT (UNIQUE)SORT (UNIQUE) 7 NESTED LOOPS1243 NESTED LOOPS1275 NESTED LOOPS1275 TABLE ACCESS (BY ROWID) OF '수주진행현황 '1276 INDEX (RANGE SCAN) OF ' 수주진행현황 _PK' (UNIQUE)1275 TABLE ACCESS (BY ROWID) OF '고객 '1275 INDEX (UNIQUE SCAN) OF ' 고객 _PK' (UNIQUE)1243 TABLE ACCESS (BY ROWID) OF '주문 '1275 INDEX (UNIQUE SCAN) OF ' 주문 _PK' (UNIQUE) 2020 TABLE ACCESS (BY ROWID) OF 'TABLE ACCESS (BY ROWID) OF '납품실행계획납품실행계획 ''12631263 INDEX (RANGE SCAN) OF ' 납품실행계획 _PK' (UNIQUE)

CC

DD

BB

AA

Page 22: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 22

SQLTuning

SQLTuning 사례 11. 조인하는 테이블의 순서에 따른 차이

1276

1276

수주진행현황C

고객D

주문B

납품실행계획A

1275

1275

1275 1275 1243

1263

1263

20 77

X

O

O

1243

NoNoNoNo X

1275

1275

Page 23: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 23

SQLTuning

SQLTuning 사례 11. 조인하는 테이블의 순서에 따른 차이

SELECT /*+ ORDERED *//*+ ORDERED */ DISTINCT A.부서번호 , A.주문번호 , B.주문일자, B.직원번호 , B.고객번호 , D.출고중지구분 FROM 수주진행현황 수주진행현황 CC , , 납품실행계획 납품실행계획 A A , 고객 고객 DD, 주문 주문 BB WHERE A.부서번호 = B.부서번호 AND A.주문번호 = B.주문번호 AND A.주문일자 = B.주문일자 AND C.AND C. 부서번호 부서번호 = = A.A. 부서번호부서번호 AND C.AND C. 주문번호 주문번호 = = A.A. 주문번호주문번호 AND C.AND C. 주문일자 주문일자 = = A.주문일자 AND C.주문번호 = B.B. 주문번호주문번호 AND C.주문일자 = B.주문일자 AND C.고객번호 = D.D. 고객번호고객번호 AND C.부서번호 = 3000 AND C.회사번호 = 3 AND A.완료여부 IS NULL ;

Page 24: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 24

SQLTuning

SQLTuning 사례 11. 조인하는 테이블의 순서에 따른 차이

1276

수주진행현황 고객 주문납품실행계획

1275 12951295 2020 20 20 20 20 77

X

X

O

NoNo

20

Page 25: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 25

SQLTuning

SQLTuning

SELECT INPTIDNO,INPTPKND,INPT,INPTLEVL,INPTMNDR,INPTDEPT,INPTCHRS,PBSCPNME, FLOOR(MONTH_BETWEEN(SYSDATE,TO_DATE(PBSCBIRT,'YYMMDD'))/12), PBSCRSEX,PBSCPHNI,CODEDESC

FROM THISCODE ATHISCODE A, THISPBSC BTHISPBSC B, THISINPT CTHISINPT C

WHERE C.INPTCHRS = SUBSTR( C.INPTCHRS = SUBSTR(A.CODENAMEA.CODENAME, 1, 1), 1, 1)

AND C.INPTDATE = :B1AND C.INPTDATE = :B1

AND C.INPTGUBN IN ('A', 'B')

AND C.INPTBYBY LIKE '1%'

AND C.INPTIDNO = B.PBSCIDNOB.PBSCIDNO

AND A.CODEGUBNA.CODEGUBN = '11';

사례 12. 인덱스 컬럼의 분리에 따른 문제 해결

인덱스정보인덱스정보THISCODE A : THISCODE_PK1 : CODEGUBNCODEGUBN +

CODENAMETHISPBSC B : THISPBSC_PK1 : PBSCIDNOPBSCIDNOTHISINPT C : THISINPT_IDX1 : INPTDATE + INPTDATE + INPTCHRSINPTCHRS

QUERYQUERY

ROWS EXECUTION PLANROWS EXECUTION PLAN----- -------------------------------------------------------

0 SELECT STATEMENT 40 NESTED LOOPS 40 NESTED LOOPS 480 TABLE ACCESS (BY ROWID) OF 'THISINPT' 481 INDEX (RANGE SCAN) OF 'THISINPT_IDX1'(NON-UNIQUE)

4040 TABLE ACCESS (BY ROWID) OF 'THISCODE'32903290 INDEX (RANGE SCAN) OF 'THISCODE_PK1'(UNIQUE)

40 TABLE ACCESS (BY ROWID) OF 'THISPBSC' 40 INDEX (UNIQUE SCAN) OF 'THISPBSC_PK1'(UNIQUE)

Access Access 비 효율의비 효율의발생 부분발생 부분

CC

AA

BB

Page 26: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 26

SQLTuning

SQLTuning

THISINPT_IDX1

INPTDATEINPTDATE+ INPTCHRS+ INPTCHRS

481481

THISINPT CC THISCODE_PK1

480480

THISCODE AA

CODEGUBNCODEGUBN+ CODENAME

THISPBSC BB

4040

THISPBSC_PK1

PBSCIDNOPBSCIDNO

4040 4040

사례 12. 인덱스 컬럼의 분리에 따른 문제 해결

32903290480480 4040

X

X

XXX

X

O

O

O

4040

Page 27: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 27

SQLTuning

SQLTuning 사례 12. 인덱스 컬럼의 분리에 따른 문제 해결

문제점문제점

모델링 잘못모델링 잘못 : : C.INPTCHRS = SUBSTR(A.CODENAME, 1, 1)C.INPTCHRS = SUBSTR(A.CODENAME, 1, 1)

조인순서조인순서 :: THISINPT(C) --> THISCODE(A) THISINPT(C) --> THISCODE(A)

해결방안해결방안THISCODE(A) --> THISINPT(C) --> THISPBSC(B)THISCODE(A) --> THISINPT(C) --> THISPBSC(B)

SELECT /*+ ORDERED *//*+ ORDERED */ INPTIDNO,INPTPKND,INPT, ..... ............ FROM THISCODE A, THISINPT C, THISPBSC BTHISCODE A, THISINPT C, THISPBSC B WHERE C.INPTCHRS = SUBSTR(C.INPTCHRS = SUBSTR(A.CODENAMEA.CODENAME, 1, 1), 1, 1) AND C.INPTDATE = :B1C.INPTDATE = :B1 AND C.INPTGUBN IN ('A','B') AND C.INPTBYBY

LIKE '1%' AND C.INPTIDNO = B.PBSCIDNOB.PBSCIDNO AND A.CODEGUBN = '11'A.CODEGUBN = '11' ;

Page 28: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 28

SQLTuning

SQLTuning 사례 13. 조인을 활용한 GROUP BY 의 처리

인덱스정보인덱스정보

SALE : SALE_PROD_NO_SALEDATE : PROD_NO + SALEDATE (NON UNIQUE)PRODUCT : PK_PRODUCT : PROD_NO (PRIMARY PRIMARY

KEYKEY)

QUERYQUERYSELECT PROD_NO, SUM ( SALE_AMT * UNIT_PRICE) FROM SALE WHERE PROD_NO BETWEENBETWEEN 'P20150' AND 'P20200' 'P20150' AND 'P20200' AND SALEDATE LIKELIKE '1995%' GROUP BY PROD_NO ;

Page 29: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 29

SQLTuning

SQLTuning 사례 13. 조인을 활용한 GROUP BY 의 처리

PROD_NO + SALEDATE

PROD_NO SALEDATE ● ● ● ●P20150P20150 19940110 ● ● ● ●P20150P20150 19950120 ● ● ● ●P20151P20151 19940102 ● ● ● ●P20151P20151 19951227 ● ● ● ●P20200P20200 19940120 ● ● ● ●P20200P20200 19951216 ● ● ● ●

SALE

PROD_NO + SALEDATE

PROD_NO SALEDATE ● ● ● ●P20150 19940110 ● ● ● ●P20150P20150 1995 19950120 ● ● ● ●P20151 19940102 ● ● ● ●P20151P20151 199519951227 ● ● ● ●P20190 19940120 ● ● ● ●P20200P20200 199519951216 ● ● ● ●

PROD_NO

P20148P20149P20150P20151 ● ● ● P20198P20199P20200 ● ● ●

PROD_NO

PRODUCT

SALE

Page 30: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 30

SQLTuning

SQLTuning 사례 14. OR 사용 시 주의할 점

인덱스정보인덱스정보CHULGOT : CHULGOT_IX : CHULDATE + ITEMCHULGOT : CH_STATUS_ITEM : STATUS + ITEMCHULGOT : CH_CHULNO : CHULNO

QUERYQUERY

(CASE 1)(CASE 1)

SELECT SUM(CNT) FROM (SELECT COUNT(*) AS CNT FROM CHULGOT WHERE :SW = 1 AND CHULDATE = '941130'WHERE :SW = 1 AND CHULDATE = '941130' UNION ALLUNION ALL SELECT COUNT(*) AS CNT FROM CHULGOT WHERE :SW = 2 AND CHULDATE+0 LIKE '96%' WHERE :SW = 2 AND CHULDATE+0 LIKE '96%' );

SELECT COUNT(*) FROM CHULGOT WHERE (:SW = 1 AND CHULDATE = '941130')(:SW = 1 AND CHULDATE = '941130') OR OR (:SW = 2 AND CHULDATE+0 LIKE '96%') (:SW = 2 AND CHULDATE+0 LIKE '96%') ;

개선된개선된 QUERYQUERY

Page 31: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 31

SQLTuning

SQLTuning 사례 14. OR 사용 시 주의할 점

SELECT COUNT(*) FROM CHULGOT WHERE CHULNO = '254'CHULNO = '254' OROR (:FILED1 + :FILED2 ) > 0 ; (:FILED1 + :FILED2 ) > 0 ;

SELECT MAXMAX(CNT) FROM ( SELECT COUNT(*) AS CNT FROM CHULGOT WHERE CHULNO = '254' CHULNO = '254' UNION ALLUNION ALL SELECT COUNT(*) AS CNT FROM CHULGOT WHERE (:FILED1 + :FILED2) > 0 (:FILED1 + :FILED2) > 0 ) ;

QUERY (CASE 2)QUERY (CASE 2)

개선된개선된 QUERYQUERY

Page 32: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 32

SQLTuning

SQLTuning 사례 14. OR 사용 시 주의할 점

SELECT --+ RULE CHULNO, CUSTNO, CHULDATE,UNCOST FROM CHULGOT WHERE STATUS LIKE DECODE( :SW, 1, '2%') STATUS LIKE DECODE( :SW, 1, '2%') <--- <--- ②② OR STATUS LIKE DECODE( :SW, 1, '1%' , '3%') ;OR STATUS LIKE DECODE( :SW, 1, '1%' , '3%') ; <--- <--- ① ① 먼저 수행먼저 수행

SELECT --+ RULE CHULNO, CUSTNO, CHULDATE,UNCOST FROM CHULGOT WHERE (:SW = 1 AND (STATUS LIKE '1%') (:SW = 1 AND (STATUS LIKE '1%') OR (:SW = 1 AND (STATUS LIKE '2%')OR (:SW = 1 AND (STATUS LIKE '2%') OR (:SW = 2 AND (STATUS LIKE '3%')OR (:SW = 2 AND (STATUS LIKE '3%') ORDER BY STATUS ;

SELECT --+ RULE CHULNO, CUSTNO, CHULDATE,UNCOST FROM CHULGOT WHERE (:SW = 1 AND AND (STATUS LIKE '1%' OR OR STATUS LIKE ‘2%’) ) OROR (:SW = 2 AND (STATUS LIKE '3%') ) ORDER BY STATUS ;

QUERY (CASE 3)QUERY (CASE 3)

개선된개선된 QUERY QUERY

11

ORDER BY 절 없음 ! 개선된개선된 QUERY QUERY

22

Page 33: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 33

SQLTuning

SQLTuning

SELECT --+ RULE CHULNO, CUSTNO, CHULDATE,UNCOST FROM CHULGOT WHERE ( :SW = 1 AND STATUS = 10 ) ( :SW = 1 AND STATUS = 10 )

OR OR ( :SW = 2 AND STATUS BETWEEN 20 AND 40 ) ( :SW = 2 AND STATUS BETWEEN 20 AND 40 ) ;

사례 14. OR 사용 시 주의할 점

SELECT --+ RULE

CHULNO, CUSTNO, CHULDATE,UNCOST

FROM CHULGOT

WHERE STATUS BETWEEN DECODE ( :SW , 1 , 10 , 2, 20 ) STATUS BETWEEN DECODE ( :SW , 1 , 10 , 2, 20 )

AND DECODE ( :SW , 1 , 10 , 2, 40 )AND DECODE ( :SW , 1 , 10 , 2, 40 ) ;

QUERY (CASE 4)QUERY (CASE 4)

개선된 개선된 QUERYQUERY

Page 34: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 34

SQLTuning

SQLTuning 사례 15. OUTER 조인의 정확한 이해

A AAB 111

B AAC 123

C ABA 222

D ABB 233

E ABC 143

A 10 AA

B 10 AB

C 10 AC

O

O

O

O

O

O

O

O

X

X

TAB1 TAB2

A AAB 111 A 10 AA

B AAC 123 B 10 AB

C ABA 222 C 10 AC

D ABB 233D ABB 233

E ABC 143E ABC 143

원하는 결과원하는 결과

KEY FLD1 FLD2 KEY COL1 COL2

인덱스정보인덱스정보

TAB1 X : TAB1_IX : FLD1 + KEYTAB2 Y : TAB2_IX : COL1 + KEY

Page 35: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 35

SQLTuning

SQLTuning 사례 15. OUTER 조인의 정확한 이해

QUERYQUERY

SELECT X.KEY, X.FLD1, X.FLD2, Y.KEY, Y.COL1, Y.COL2 FROM TAB1 X, TAB2 Y WHERE X.KEY = Y.KEY(+)X.KEY = Y.KEY(+) AND X.FLD1 > 'AAA' AND Y.COL1 = 10 Y.COL1 = 10 ;

SELECT X.KEY, X.FLD1, X.FLD2, Y.KEY, Y.COL1, Y.COL2 FROM TAB1 X, TAB2 Y WHERE X.KEY = Y.KEY(+)X.KEY = Y.KEY(+) AND X.FLD1 > 'AAA' AND (Y.COL1 = 10 OR Y.COL1 IS NULL) ; (Y.COL1 = 10 OR Y.COL1 IS NULL) ;

SELECT X.KEY, X.FLD1, X.FLD2, Y.KEY, Y.COL1, Y.COL2 FROM TAB1 X, TAB2 Y WHERE X.KEY = Y.KEY(+)X.KEY = Y.KEY(+) AND X.FLD1 > 'AAA' AND Y.COL1Y.COL1(+)(+) = 10 ; = 10 ;

원하는 결과 나오지 않음

COL1+KEY 순서이므로 인덱스 사용 불가 KEY+COL1 이면 인덱스 사용 가능

COL1+KEY 인덱스 사용 가능 KEY+COL1 이라도 사용 가능

TAB1 X : TAB1_IX : FLD1 + KEYTAB2 Y : TAB2_IX : COL1 + KEY

Page 36: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 36

SQLTuning

SQLTuning 사례 16. 복잡한 로직을 OUTER 조인으로 해결

카드엠보싱내역 테이블 # * 카드번호 # * 일련번호 유형 접수일자 발행일자 영업점번호 담당자 ID

소개 소개 : : OUTER 조인과 DECODE 를 이용하여 복잡한 로직을 간단히 해결

요구 사항요구 사항 입력된 카드번호에 엠보싱이 요구될 경우 최초 엠보싱일 경우에는

일련번호를 1 로 셋팅하고 , 이미 엠보싱이 된 경우에는 입력된 카드번호의 일련번호 중 최대 번호 (MAX 일련번호 ) 를 찾아서 그 번호에 +1 을 하여 카드엠보싱내역에 INSERT 한다 .

Page 37: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 37

SQLTuning

SQLTuning 사례 16. 복잡한 로직을 OUTER 조인으로 해결

EXEC SQL SELECT MAX( 일련번호 ) + 1 INTO : 다음일련번호 FROM 카드엠보싱내역 WHERE 카드번호 = : 입력카드번호 ;

IF (SQLCA.SQLCODE = = 1403) /* 카드번호 NOT FOUND */THEN EXEC SQL INSERT INTO 카드엠보싱내역 VALUES( : 입력카드번호 , 1, : 유형 , : 접수일자 , : 발행일자 , : 영업점번호 , : 담당자 ID ) ;ELSE EXEC SQL INSERT INTO 카드엠보싱내역 VALUES( : 입력카드번호 , : 다음일련번호 , : 유형 , :

접수일자 , : 발행일자 , : 영업점번호 , : 담당자 ID ) ;

일반적인 로직일반적인 로직

Page 38: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 38

SQLTuning

SQLTuning 사례 16. 복잡한 로직을 OUTER 조인으로 해결

• 개선된 QUERY

OUTER JOIN 을 사용하여 한번에 INSERT 로 작업 수행

INSERT INTO 카드엠보싱내역 SELECT /*+ INDEX_DESC(B TBCARDEMBO_PK) USE_NL(A B) *//*+ INDEX_DESC(B TBCARDEMBO_PK) USE_NL(A B) */ : 입력카드번호 , DECODE(B. 카드번호 , NULL, 1, B. 일련번호 +1),

: 유형 , : 접수일자 , : 발행일자 , : 담당자 ID FROM 카드엠보싱내역 B, (SELECT :(SELECT : 입력카드번호 입력카드번호 AS AS 입력카드번호 입력카드번호 FROM DUAL) AFROM DUAL) A WHERE A.A. 입력카드번호 입력카드번호 = B.= B. 카드번호카드번호 (+)(+) AND ROWNUM = 1 ;AND ROWNUM = 1 ;

SORT MERGE 로 풀리는 것 방지

Page 39: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 39

SQLTuning

SQLTuning 사례 17. DECODE 를 활용한 SQL 통합

QUERYQUERY

SELECT NVL(SUM(DACT_MH),0), NVL(SUM(DACT_MH),0), NVL(SUM(DACT_AMT),0)NVL(SUM(DACT_AMT),0)FROM S_DAY_ACTSCHWHERE ORDERNO = :B1 AND ITEMNO = :B2;

SELECT NVL(SUM(DACT_MH),0), NVL(SUM(DACT_MH),0), NVL(SUM(DACT_AMT),0)NVL(SUM(DACT_AMT),0)FROM S_DAY_ACTSCHWHERE ORDERNO = :B1 AND ITEMNO = :B2 AND CON_CODE IS NOT NULL;AND CON_CODE IS NOT NULL;

SELECT NVL(SUM(DACT_MH),0), NVL(SUM(DACT_MH),0), NVL(SUM(DACT_AMT),0)NVL(SUM(DACT_AMT),0)FROM S_DAY_ACTSCHWHERE ORDERNO = :B1 AND ITEMNO = :B2; AND (CON_CODE IS NULL OR AND (CON_CODE IS NULL OR LENGTH(CON_CODE) = 1 );LENGTH(CON_CODE) = 1 );

Page 40: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 40

SQLTuning

SQLTuning 사례 17. DECODE 를 활용한 SQL 통합

SELECT NVL(SUM(DACT_MH ),0), NVL(SUM(DACT_AMT),0), NVL(SUM(DECODE( CON_CODE, NULL, 0, DACT_MH ))SUM(DECODE( CON_CODE, NULL, 0, DACT_MH )),0), NVL(SUM(DECODE( CON_CODE, NULL, 0, DACT_AMT))SUM(DECODE( CON_CODE, NULL, 0, DACT_AMT)),0), NVL(SUM(DECODESUM(DECODE( ( CON_CODE, NULL, DACT_MH,CON_CODE, NULL, DACT_MH, DECODE(LENGTH(CON_CODE), 1, DACT_MH, 0) DECODE(LENGTH(CON_CODE), 1, DACT_MH, 0) )))), 0), NVL(SUM(DECODESUM(DECODE( ( CON_CODE, NULL, DACT_AMTCON_CODE, NULL, DACT_AMT, DECODE(LENGTH(CON_CODE), 1, DACT_AMT,0) DECODE(LENGTH(CON_CODE), 1, DACT_AMT,0) )))), 0),FROM S_DAY_ACTSCHWHERE ORDERNO = :B1 AND ITEMNO = :B2;

개선된 개선된 QUERYQUERY

Page 41: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 41

SQLTuning

SQLTuning 사례 18. 과도한 DECODE 의 사용

QUERYQUERY

SELECT B.TM, M.TAX, M.PUMMOK, SUM(DECODE(NAPBAN, '1', P.SSU, 0)), SUM(DECODE(NAPBAN, '1', P.KUMEK1, 0)), SUM(DECODE(NAPBAN, '1', DECODE(B.BOKWAN, '1', P.KUMEK2, 0))), SUM(DECODE(NAPBAN, '1', DECODE(B.BOKWAN, '1', P.KUMEK2, 0))), SUM(DECODE(NAPBAN, '1', DECODE(B.BOKWAN, '2', P.KUMEK2, 0))), SUM(DECODE(NAPBAN, '1', DECODE(B.BOKWAN, '2', P.KUMEK2, 0))), SUM(DECODE(NAPBAN, '1', DECODE(B.BOKWAN, '3', P.KUMEK2, 0))), SUM(DECODE(NAPBAN, '1', DECODE(B.BOKWAN, '3', P.KUMEK2, 0))), SUM(DECODE(NAPBAN, '2', P.SSU, 0)), SUM(DECODE(NAPBAN, '2', P.KUMEK1, 0)), SUM(DECODE(NAPBAN, '2', DECODE(B.BOKWAN, '1', P.KUMEK2, 0))), SUM(DECODE(NAPBAN, '2', DECODE(B.BOKWAN, '2', P.KUMEK2, 0))), SUM(DECODE(NAPBAN, '2', DECODE(B.BOKWAN, '3', P.KUMEK2, 0))), SUM(DECODE(NAPBAN, '3', P.SSU, 0)), SUM(DECODE(NAPBAN, '3', P.KUMEK1, 0)), SUM(DECODE(NAPBAN, '3', DECODE(B.BOKWAN, '1', P.KUMEK2, 0))), SUM(DECODE(NAPBAN, '3', DECODE(B.BOKWAN, '2', P.KUMEK2, 0))), SUM(DECODE(NAPBAN, '3', DECODE(B.BOKWAN, '3', P.KUMEK2, 0))), FROM GGB M, GPM P, MJ B WHERE B.YT = P.YT AND B.MAEJANG = P.MAEJANG AND M.CODE = P.PUMMOK AND P.DATE BETWEEN :B1 AND :B2 GROUP BY B.TM, M.TAX, M.PUMMOKGROUP BY B.TM, M.TAX, M.PUMMOK ORDER BY B.TM, M.TAX, M.PUMMOK ;ORDER BY B.TM, M.TAX, M.PUMMOK ;

Page 42: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 42

SQLTuning

SQLTuning 사례 18. 과도한 DECODE 의 사용

SELECT B.TM, M.TAX, M.PUMMOK, SUM(DECODE(NAPBAN, '1', P.SSU, 0)), SUM(DECODE(NAPBAN, '1', P.KUMEK1, 0)), SUM(DECODE(NAPBAN||B.BOKWAN, '11', P.KUMEK2, 0)), SUM(DECODE(NAPBAN||B.BOKWAN, '11', P.KUMEK2, 0)), SUM(DECODE(NAPBAN||B.BOKWAN, '12', P.KUMEK2, 0)), SUM(DECODE(NAPBAN||B.BOKWAN, '12', P.KUMEK2, 0)), SUM(DECODE(NAPBAN||B.BOKWAN, '13', P.KUMEK2, 0)),SUM(DECODE(NAPBAN||B.BOKWAN, '13', P.KUMEK2, 0)), SUM(DECODE(NAPBAN, '2', P.SSU, 0)), SUM(DECODE(NAPBAN, '2', P.KUMEK1, 0)), SUM(DECODE(NAPBAN||B.BOKWAN, '21', P.KUMEK2, 0)), SUM(DECODE(NAPBAN||B.BOKWAN, '22', P.KUMEK2, 0)), SUM(DECODE(NAPBAN||B.BOKWAN, '23', P.KUMEK2, 0)), SUM(DECODE(NAPBAN, '3', P.SSU, 0)), SUM(DECODE(NAPBAN, '3', P.KUMEK1, 0)), SUM(DECODE(NAPBAN||B.BOKWAN, '31', P.KUMEK2, 0)), SUM(DECODE(NAPBAN||B.BOKWAN, '32', P.KUMEK2, 0)), SUM(DECODE(NAPBAN||B.BOKWAN, '33', P.KUMEK2, 0)), FROM GGB M, GPM P, MJ B WHERE B.YT = P.YT AND B.MAEJANG = P.MAEJANG AND M.CODE = P.PUMMOK AND P.DATE BETWEEN :B1 AND :B2 GROUP BY B.TM, M.TAX, M.PUMMOKGROUP BY B.TM, M.TAX, M.PUMMOK ;

개선된 개선된 QUERYQUERY

Page 43: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 43

SQLTuning

SQLTuning 사례 19. 비숫한 SQL 을 통합하여 SUM(DECODE) 로

소개 소개 : : WHERE 조건이 조금 다르면서 동일한 테이블을 사용하는 SQL 통합

유사한 유사한 SQLSQL

EXEC SQL SELECT MAX(LINE_NO) INTO :TOT_LINE_NO FROM BA004DM WHERE BRK_CD= :BRK_CD AND DCL_YEAR = :DCL_YEAR AND SBM_SRL_NO = :SBM_SRL_NO AND DITC = :DITC ;

EXEC SQL SELECT COUNT(LINE_NO) INTO :M_LINE_NO FROM BA004DM WHERE BRK_CD = :BRK_CD AND DCL_YEAR = :DCL_YEAR AND SBM_SRL_NO = :SBM_SRL_NO AND DITC = :DITC AND LINE_NO < 0AND LINE_NO < 0 ;

Page 44: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 44

SQLTuning

SQLTuning 사례 19. 비숫한 SQL 을 통합하여 SUM(DECOD

E) 로

EXEC SQL SELECT MAX (LINE_NO) , SUM(DECODE(SIGN(LINE_NO), -1, 1 )) INTO :TOT_LINE_NO, :M_LINE_NO FROM BA004DM WHERE BRK_CD= :BRK_CD AND DCL_YEAR = :DCL_YEAR AND SBM_SRL_NO = :SBM_SRL_NO AND DITC = :DITC ;

해결 방안해결 방안

Page 45: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 45

SQLTuning

SQLTuning 사례 20. DECODE 와 GROUP BY 의 효율적인

사용

자격등록마스터 테이블 (약 100 만 건) 요구 사항

등록자 ID QUAL_ID NUMBER PRIMARY KEY

등록자명 QUAL_NM VARCHAR2(10)

직종코드 JOB_CLSS CHAR(2)

성별 SEX CHAR(1)

등록일자 REG_DATE CHAR(8)

급여 SALARY NUMBER

가족수 FAMILY_CNT NUMBER

등급 GRAD_CLSS CHAR(1)

1996 년도 자료 중 직종별,성별로 구분하여 등록 월별로등록된 건수를 표시하라.

[힌트]

GROUP BY 직종별, 성별이 되어야 하고 세로로 된 데이터를 가로로 펼치기 위해DECODE를 사용한다.

Page 46: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 46

SQLTuning

SQLTuning

SELECT JOB_CLSS, SEX, SUM(DECODE(SUBSTR(REG_DATE,1,6),’199601’,1,0)) AS 9601 월 , SUM(DECODE(SUBSTR(REG_DATE,1,6),’199602’,1,0)) AS 9602 월 ,

............ SUM(DECODE(SUBSTR(REG_DATE,1,6),’199612’,1,0)) AS 9612 월 FROM QUALIFIER GROUP BY JOB_CLSS, SEX;

사례 20. DECODE 와 GROUP BY 의 효율적인 사용

2 분 24 초

병렬 처리병렬 처리

SELECT /*+ FULL(QUALIFIER) PARALLEL(QUALIFIER 2) *//*+ FULL(QUALIFIER) PARALLEL(QUALIFIER 2) */ JOB_CLSS, SEX, SUM(DECODE(SUBSTR(REG_DATE,1,6),’199601’,1,0)) AS 9601 월 ,

............ FROM QUALIFIER GROUP BY JOB_CLSS, SEX ORDER BY 1, 2ORDER BY 1, 2; 1 분 13 초

Page 47: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 47

SQLTuning

SQLTuning

SELECT JC, SEX, SUM(DECODE(REG_MM,'199601',CNT,0)) AS 9601 월 , SUM(DECODE(REG_MM,'199602',CNT,0)) AS 9602 월 , SUM(DECODE(REG_MM,'199603',CNT,0)) AS 9603 월 , SUM(DECODE(REG_MM,'199604',CNT,0)) AS 9604 월 , ........ SUM(DECODE(REG_MM,'199610',CNT,0)) AS 9610 월 , SUM(DECODE(REG_MM,'199611',CNT,0)) AS 9611 월 , SUM(DECODE(REG_MM,'199612',CNT,0)) AS 9612 월FROM (SELECT /*+ FULL(QUALIFIER) PARALLEL(QUALIFIER,2) *//*+ FULL(QUALIFIER) PARALLEL(QUALIFIER,2) */ JOB_CLSS AS JC, SEX, SUBSTR(REG_DATE,1,6) AS REG_MM, COUNT(*) AS CNT FROM QUALIFIER GROUP BY JOB_CLSS, SEX, SUBSTR(REG_DATE,1,6)GROUP BY JOB_CLSS, SEX, SUBSTR(REG_DATE,1,6) )GROUP BY JC, SEX ;

사례 20. DECODE 와 GROUP BY 의 효율적인 사용

Parallel: 14 14 초초

No Parallel : 27No Parallel : 27 초초

Page 48: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 48

SQLTuning

SQLTuning

소개 소개 : : 데이터 복제를 이용하여 기준이 되는 항목에 대하여 데이터가 없어도 빠짐없이 모두 표시

사례 21. 데이터 복제를 이용하여 빠짐없이 표시

위와 같은 환경하에 모든 지역에 대하여 한주일 동안 자격을 취득한 모든 사람을 지역별 , 성별로 구분하여 표시

원하는 기간에 대해 해당지역에 자격취득자가 없는 경우에도 남녀 항목이 생성되어 “ 0” 이라는 값을 가짐

피보험자격정보

# * 주민등록번호 성명 자격취득일자 지역코드 (FK) 주소 성별

지역정보

# * 지역코드 지역명 대표지부장

Page 49: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 49

SQLTuning

SQLTuning 사례 21. 데이터 복제를 이용하여 빠짐없이 표시

표시 예표시 예

지역명 성 별 기준일 +1 +2 +3 +4 +5 +6

서울

서울

.....

경남

경남

제주

제주

.....

1

2

.....

1

0

0

0

0

1

.....

1

0

0

0

3

.....

1

0

0

1

3

4

.....

2

0

0

0

2

1

.....

1

0

0

0

1

0

.....

0

0

0

0

0

1

.....

0

0

0

0

Page 50: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 50

SQLTuning

SQLTuning 사례 21. 데이터 복제를 이용하여 빠짐없이 표시

간단한 간단한 SQLSQL

SELECT 지역명 , 성별 , SUM(DECODE( 자격취득일자 , : 입력일자 , 1), SUM(DECODE( 자격취득일자 , : 입력일자 +1, 1), SUM(DECODE( 자격취득일자 , : 입력일자 +2, 1), SUM(DECODE( 자격취득일자 , : 입력일자 +3, 1), SUM(DECODE( 자격취득일자 , : 입력일자 +4, 1), SUM(DECODE( 자격취득일자 , : 입력일자 +5, 1), SUM(DECODE( 자격취득일자 , : 입력일자 +6, 1), FROM 피보험자자격정보 A, 지역정보 B WHERE A. 자격취득일자 BETWEEN : 입력일자 AND : 입력일자 +6 AND A. 지역코드 = B. 지역코드 GROUP BY B. 지역명 , A. 성별 ;

문제점 문제점 : : “ 경남지역 여”나 “제주지역 남”처럼 자격을 취득한 로우가 없으면 해당로우가 나타나지 않음

Page 51: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 51

SQLTuning

SQLTuning 사례 21. 데이터 복제를 이용하여 빠짐없이 표시

해결 방안 해결 방안

서울 남 1 0 5 3 2 1 0

서울 여 2 1 3 4 1 0 1

경남 남 1 0 1 2 1 0 0

제주 여 0 0 1 0 0 0 0

GROUP BY 지역명 , 성별

+

남 여

서울 남 ..... 남서울 남 ..... 여서울 여 ..... 남서울 여 ..... 여경남 남 ..... 남경남 남 ..... 여제주 여 ..... 남제주 여 ..... 여

서울 남 ..... : 남서울 남 0....0 : 여서울 여 0....0 : 남서울 여 ..... : 여경남 남 ..... : 남경남 남 0....0 : 여제주 여 0....0 : 남제주 여 ..... : 여

Join

Deco

de

Gro

up

By

Page 52: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 52

SQLTuning

SQLTuning 사례 21. 데이터 복제를 이용하여 빠짐없이 표시

SQLSQLSELECT V. 지역명 ,R. 성별 , SUM (DECODE(V.SUM (DECODE(V. 성별성별 ,R.,R. 성별성별 ,SUM1,0) AS ,SUM1,0) AS 기준일 기준일 ,, SUM (DECODE(V.SUM (DECODE(V. 성별성별 ,R.,R. 성별성별 ,SUM1,0) AS ,SUM1,0) AS 기준일기준일 +1,+1, SUM (DECODE(V.SUM (DECODE(V. 성별성별 ,R.,R. 성별성별 ,SUM1,0) AS ,SUM1,0) AS 기준일기준일 +2,+2, SUM (DECODE(V.SUM (DECODE(V. 성별성별 ,R.,R. 성별성별 ,SUM1,0) AS ,SUM1,0) AS 기준일기준일 +3,+3, SUM (DECODE(V.SUM (DECODE(V. 성별성별 ,R.,R. 성별성별 ,SUM1,0) AS ,SUM1,0) AS 기준일기준일 +4,+4, SUM (DECODE(V.SUM (DECODE(V. 성별성별 ,R.,R. 성별성별 ,SUM1,0) AS ,SUM1,0) AS 기준일기준일 +5,+5, SUM (DECODE(V.SUM (DECODE(V. 성별성별 ,R.,R. 성별성별 ,SUM1,0) AS ,SUM1,0) AS 기준일기준일 +6,+6,FROM (SELECT B. 지역명 , A. 성별 , SUM (DECODE(SUM (DECODE( 자격취득일자자격취득일자 , :, : 입력일자 입력일자 , 1) AS SUM1,, 1) AS SUM1, SUM (DECODE(SUM (DECODE( 자격취득일자자격취득일자 , :, : 입력일자입력일자 +1, 1) AS SUM2,+1, 1) AS SUM2, SUM (DECODE(SUM (DECODE( 자격취득일자자격취득일자 , :, : 입력일자입력일자 +2, 1) AS SUM3,+2, 1) AS SUM3, SUM (DECODE(SUM (DECODE( 자격취득일자자격취득일자 , :, : 입력일자입력일자 +3, 1) AS SUM4,+3, 1) AS SUM4, SUM (DECODE(SUM (DECODE( 자격취득일자자격취득일자 , :, : 입력일자입력일자 +4, 1) AS SUM5,+4, 1) AS SUM5, SUM (DECODE(SUM (DECODE( 자격취득일자자격취득일자 , :, : 입력일자입력일자 +5, 1) AS SUM6,+5, 1) AS SUM6, SUM (DECODE(SUM (DECODE( 자격취득일자자격취득일자 , :, : 입력일자입력일자 +6, 1) AS SUM7+6, 1) AS SUM7 FROM 피보험자자격정보 A, 지역정보 B WHERE A. 자격취득일자 BETWEEN : 입력일자 AND : 입력일자 + 6 AND A. 지역코드 = B. 지역코드 GROUP BY B.GROUP BY B. 지역명 지역명 , A., A. 성별성별 ) V, (SELECT '(SELECT ' 남남 ' AS ' AS 성별 성별 FROM DUALFROM DUAL UNION ALLUNION ALL SELECT 'SELECT ' 여여 ' AS ' AS 성별 성별 FROM DUAL) RFROM DUAL) RGROUP BY V.GROUP BY V. 지역명지역명 , R., R. 성별성별 ;

Page 53: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 53

SQLTuning

SQLTuning 사례 21. 데이터 복제를 이용하여 빠짐없이 표시

서울 남 1 0 5 3 2 1 0

서울 남 1 0 5 3 2 1 0

서울 여 2 1 3 4 1 0 1

서울 여 2 1 3 4 1 0 1

경남 남 1 0 1 2 1 0 0

경남 남 1 0 1 2 1 0 0

제주 여 0 0 1 0 0 0 0

제주 여 0 0 1 0 0 0 0

DDEECCOODDEE

서울 남 1 0 5 3 2 1 0 남

서울 남 0 0 0 0 0 0 0 여

서울 여 0 0 0 0 0 0 0 남

서울 여 2 1 3 4 1 0 1 여

경남 남 1 0 1 2 1 0 0 남

경남 남 0 0 0 0 0 0 0 여

제주 여 0 0 0 0 0 0 0 남

제주 여 0 0 1 0 0 0 0 여

V. 지역명 R. 성별V.SUM1

GROUP BYGROUP BY

GROUP BYGROUP BY

GROUP BYGROUP BY

GROUP BYGROUP BY

Page 54: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 54

SQLTuning

SQLTuning 사례 21. 데이터 복제를 이용하여 빠짐없이 표시

SQLSQL

SELECT V. 지역명 , R. 성별 , SUM (DECODE(V. 성별 ,R. 성별 ,SUM1,0) AS 기준일 , SUM (DECODE(V. 성별 ,R. 성별 ,SUM1,0) AS 기준일 +1, SUM (DECODE(V. 성별 ,R. 성별 ,SUM1,0) AS 기준일 +2, SUM (DECODE(V. 성별 ,R. 성별 ,SUM1,0) AS 기준일 +3, SUM (DECODE(V. 성별 ,R. 성별 ,SUM1,0) AS 기준일 +4, SUM (DECODE(V. 성별 ,R. 성별 ,SUM1,0) AS 기준일 +5, SUM (DECODE(V. 성별 ,R. 성별 ,SUM1,0) AS 기준일 +6,FROM (SELECT B. 지역명 , A. 성별 , SUM (DECODE( 자격취득일자 , : 입력일자 , 1) AS S

UM1, SUM (DECODE( 자격취득일자 , : 입력일자 +1, 1) AS SUM

2, SUM (DECODE( 자격취득일자 , : 입력일자 +2, 1) AS SUM

3, SUM (DECODE( 자격취득일자 , : 입력일자 +3, 1) AS SUM

4, SUM (DECODE( 자격취득일자 , : 입력일자 +4, 1) AS SUM

5, SUM (DECODE( 자격취득일자 , : 입력일자 +5, 1) AS SUM

6, SUM (DECODE( 자격취득일자 , : 입력일자 +6, 1) AS SUM

7 FROM (SELECT 자격취득일자 , 성별 , 지역코드 FROM 피보험자자격정보피보험자자격정보 WHERE 자격취득일자 자격취득일자 BETWEEN :BETWEEN : 입력일자 입력일자 AND :AND : 입력일자입력일자 +6 ) A,+6 ) A, 지역정보 B WHERE B.WHERE B. 지역코드 지역코드 = A.= A. 지역코드지역코드 (+) (+) GROUP BY B. 지역명 , A. 성별 ) V, (SELECT ' 남 ' AS 성별 FROM DUAL UNION ALL SELECT ' 여 ' AS 성별 FROM DUAL) RGROUP BY V. 지역명 , R. 성별 ;

Page 55: 사례  1.  년 / 월이 분리된 경우에도 인덱스 사용

DB 기술자문팀 55

SQLTuning

SQLTuning