2. ROW ID 조인 시 순서
ROWID 구조
블록간에 유일한 구분 값을 위해 블록 주소 정의
ORACLE에서 INDEX를 생성하기 위해 내부적으로 사용하는 키로 임의 변경,삭제 불가
ROWID는 테이블 형태로 관리 되지 않고 식별키로만 사용
테이블 ROW에 접근하는 가장 빠른 수단
테이블 열 들을(ROWS) 위한 유일한 식별자
3. ROW ID 조인 시 순서
SELECT ROWID,
MBR_ID
FROM MBR_MEMBER_MST
AAAX5JAARAAB83rAAJ 1000457
AAAX5JAAQAAChKRAAp 1000492
AAAX5JAAQAAChR4AAR 1001132
AAAX5JAARAAB87sAAe 1001135
AAAX5JAARAAB82qAAn 1001144
AAAX5JAARAAB82rAAI 1001505
AAAX5JAAQAAChU7AAa 1001558
AAAX5JAAQAAChU7AAY 1001565
SELECT ROWID,
MBR_ID
FROM COM_LOGIN_MST
AAAXpyAAQAACgWbAAB jangreen
AAAXpyAAQAACgWbAAC ngh8751
AAAXpyAAQAACgWbAAD 70533545
AAAXpyAAQAACgWbAAE oji1602
AAAXpyAAQAACgWbAAF chong1210
AAAXpyAAQAACgWbAAG shin0420
AAAXpyAAQAACgWbAAH kjs89
AAAXpyAAQAACgWbAAI choeun90
SELECT ROWID,
A.MBR_ID, B.LOGIN_ID
FROM MBR_MEMBER_MST A,
COM_LOGIN_ID_MST B
WHERE A.MBR_ID = B.MBR_ID
SELECT A.ROWID, B.ROWID
A.MBR_ID, B.LOGIN_ID
FROM MBR_MEMBER_MST A,
COM_LOGIN_ID_MST B
WHERE A.MBR_ID = B.MBR_ID
4. ROW ID 조인 시 순서
SELECT ROWID, A.*
FROM (
SELECT A.ROWID, B.ROWID
A.MBR_ID, B.LOGIN_ID
FROM MBR_MEMBER_MST A,
COM_LOGIN_ID_MST B
WHERE A.MBR_ID = B.MBR_ID
) A
SELECT ROWID, A.*
FROM (
SELECT A.MBR_ID, B.LOGIN_ID
FROM MBR_MEMBER_MST A,
COM_LOGIN_ID_MST B
WHERE A.MBR_ID = B.MBR_ID
) A
AAAXpyAAQAACgWbAAB 167132jangreen
AAAXpyAAQAACgWbAAC 187917ngh8751
AAAXpyAAQAACgWbAAD 18398870533545
AAAXpyAAQAACgWbAAE 160764oji1602
AAAXpyAAQAACgWbAAF 186335chong1210
AAAXpyAAQAACgWbAAG 161908shin0420
AAAXpyAAQAACgWbAAH 177346kjs89
AAAXpyAAQAACgWbAAI 165904choeun90
A B
AAAX5JAANAAHwjkAAV AAAXpyAAQAACgWbAAB 167132 jangreen
AAAX5JAANAAHwjkAAW AAAXpyAAQAACgWbAAC 187917 ngh8751
AAAX5JAANAAHwjkAAX AAAXpyAAQAACgWbAAD 183988 70533545
AAAX5JAANAAHwjkAAY AAAXpyAAQAACgWbAAE 160764 oji1602
AAAX5JAANAAHwjnAAb AAAXpyAAQAACgWbAAF 186335 chong1210
AAAX5JAANAAHwjnAAc AAAXpyAAQAACgWbAAG 161908 shin0420
AAAX5JAANAAHwjnAAd AAAXpyAAQAACgWbAAH 177346 kjs89
AAAX5JAANAAHwjnAAe AAAXpyAAQAACgWbAAI 165904 choeun90
ROWID가 작은게 출력
5. 서브쿼리가 비효율적 인가?
실행 계획을 볼 줄 알면 서브쿼리가 비효율적으로 나올 수 없습니다.
서브쿼리는 집합으로 묶어주는 기능일 뿐이지 성능을 비효율적으로 만들지 않습니다.
멀티캐시 효과 때문에 경우에 따라 오히려 퍼포먼스를 낼 수 있습니다.
서브쿼리는 읽는 범위가 작아야 합니다.
6. 서브쿼리가 비효율적 인가?
SELECT DEPT_NO, COUNT(*) CNT,
(SELECT COUNT(*) FROM EMP) TOTALEMP
FROM EMP
GROUP BY DEPT_NO
변경 후
SELECT DEPTNO, DNAME
,TO_NUMBER(SUBSTR(VAL, 1,10)) AVG_SAL
,TO_NUMBER(SUBSTR(VAL, 11,10)) MIN_SAL
,TO_NUMBER(SUBSTR(VAL, 21)) MAX_SAL
FROM (
SELECT D.DEPTNO, D.DNAME,
(SELECT LPAD(AVG(SAL),10)||LPAD(MIN(SAL),10)||MAX(SAL)
FROM EMP
WHERE DEPTNO = D.DEPTNO)VAL
FROM DEPT D
WHERE D.LOC = 'LA'
)
안 좋은 예)
변경 전
SELECT D.DEPTNO, D.DNAME. AVG_SAL, MIN_SAL, MAX_SAL
FROM DEPT D,
(SELECT DEPTNO, AVG(SAL) AVG_SAL, MIN(SAL) MIN_SAL, MAX(SAL) MAX_SAL
FROM EMP
GROUP BY DEPTNO)E
WHERE E.DEPTNO(+) = D.DEPTNO
AND D.LOC = 'LA'
-불필요한 사원 집합 발생
-OUTER JOIN으로 인한 E 드라이빙
-사원 테이블에 넓은 영역 집합 함수
-효율적인 사원 집합 추출
-OUTER JOIN을제거하여 D부터 드라이빙
-사원 테이블에 좁은 영역에 집합 함수
-서브쿼리를 CONCATENATE 사용
8. Programming vs SQL
Programming
SQL
public int aa(int a, int b)throws Exception{
int c = 0;
if(a == b){
return a;
}else{
return b;
}
}
public String getMaxNumber(int iNum1, int iNum2)throws Exception{
return iNum1 > iNum2 ? iNum1 : iNum2;
}
SELECT *
FROM (
SELECT ROWNUM RN , A.*
FROM EMP A
WHERE ENAME LIKE ‘강%’
AND JOB <> ‘SALES’
AND (STATUS = ‘1’ OR STATUS = ‘2’)
)
WHERE RN BETWEEN 1 AND 10
SELECT *
FROM
(
SELECT ROWNUM RN , A.*
FROM EMP A
WHERE ENAME LIKE ‘강%’
AND NOT EXISTS(SELECT 1 FROM EMP B
WHERE B.EMPNO = A.EMPNO
AND JOB = ‘SALES’
AND ROWNUM = 1)
AND STATUS IN (‘1’, ‘2’)
AND ROWNUM <= 10
)
WHERE RN >= 1
9. 절차형 사고 버리기
SELECT *
FROM MNS A,
SKP B,
SKT C
WHERE A.조건 = B.조건
AND B.조건 = C.조건
AND C.조건 = ‘1234’
10. Driving의 중요성
TABLE1 TABLE2 TABLE3
(10000 row)
(1000 row)
(2 row)
. . .
1 A
2 C
3 D
4 K
5 M
6 F
7 E
8 M
. . . .
. . . .
A 가
P 나
C 라
H 사
. . .
E 마
라 10
마 20
최소 10,000회 이상 ACCESS
TABLE3 TABLE2 TABLE1
(10000 row)
(2 row)
라 10
마 20
(1000 row)
A 가
P 나
C 라
S 마
. . .
E 마
1 A
2 C
3 D
4 K
5 M
6 F
7 E
8 M
. . . .
. . . .
최대 6회 이하 ACCESS
11. Driving의 중요성
TABLE1 TABLE2 TABLE3
(10000 row)
(1000 row)
(2 row)
. . .
1 A
2 C
3 D
4 K
5 M
6 F
7 E
8 M
. . . .
. . . .
A 가
P 나
C 라
H 사
. . .
E 마
라 10
마 20
-TABLE1을 무조건 읽어야 한다면 그 다음에 올 DRIVNING 순서를 예측
-똑같은 조건 이라면 M이 아닌 1 부터 DRIVING
TABLE1 TABLE3 TABLE2
(1000 row)
(10000 row)
(2 row)
. . .
1 A
2 C
3 D
4 K
5 M
6 F
7 E
8 M
. . . .
. . . .
C 10
E 20
A 가
P 나
C 라
S 마
. . .
E 마
12. Optimizer
select col1,
col2*10, . .
from account x,
custommer y,
transection z
where x.acct = z.acct
and y.cust = z.cust
and jdate = ‘130319’;
SQL OPTIMIZER
DATA Dictionary
SQL
해석
COL$
IND$
OBJ$
TAB$
VIEW$
참
조
실행
customer
transaction
account
DATA
추
출
실행
계획
작성
참
조
ㄴㅍㄴㅇㄹㅇㄹㄴ 률ㄷㄱ34346
ㅓㅏㄴ아ㅓㅗㄴㅇ ㅓㅜㄴ야ㄷㅈㄷㅂ저
ㅊ리아ㅡㄹ ㅏㅡ치ㅏ ㅜ ㅓ투 93
ㅑㅇ너ㅓㅇㄹ너ㅐㅇ러ㅐㄿㄹㅇㄹ ㅓㅜㄴ ㅑ
ㅑㅕㅜㅑ ㅜ랸웅ㄴ
ㅑ어ㅐ우ㅐㅇ눈애ㅓ래ㅓㅐ앵래 8ㅈ9ㅗ9 ㅗㅑ야 ㅏㄴ어ㅐ
B BB JHBJB M M J ㅐㅜ ㅜㄹ울
애ㅣㅓ애럴애ㅓㄹ애 ㅐ
ㄹ앙ㄹㄹ이ㅏㅡㅈ냐ㅈㄷㅂ989ㅈ돌ㅍㄴㅇㄴ
ㄴ어ㅐㅑㅓ내ㅑ
ㄴㅍㄴㅇㄹㅇㄹㄴ 률ㄷㄱ34346
ㅓㅏㄴ아ㅓㅗㄴㅇ ㅓㅜㄴ야ㄷㅈㄷㅂ저
ㅊ리아ㅡㄹ ㅏㅡ치ㅏ ㅜ ㅓ투 93
ㅑㅇ너ㅓㅇㄹ너ㅐㅇ러ㅐㄿㄹㅇㄹ ㅓㅜㄴ
ㅑㅕㅇ ㅑㅕㅜㅑ ㅜ랸웅ㄴ
ㅑ어ㅐ우ㅐㅇ눈애ㅓ래ㅓㅐ앵래 8ㅈ9ㅗ9 ㅗ
ㅑ야 ㅏㄴ어ㅐㅑ퍼ㅐㅜ ㅜㄹ울
애ㅣㅓ애럴애ㅓㄹ애 ㅐ
ㄹ앙ㄹㄹ이ㅏㅡㅈ냐ㅈㄷㅂ
결
과
사용자는 요구만 하고 OPTIMIZER가
실행계획 수립
수립된 실행계획에 따라 엄청난 수행
속도 차이 발생
실행계획 제어가 어렵다.
OPTIMIZER가 좋은 실행계획을 수립
할 수 있도록 종합적이고 전략적인
FACTOR를 부여
비절차형으로 기술해야 함
집합적으로 접근해야 함
14. Optimizer
SELECT * FROM EMP
WHERE ENAME LIKE 'AB%'
AND EMPNO = '7890'
SELECT * FROM EMP
WHERE ENAME LIKE 'AB%'
AND JOB LIKE 'SA%'
Rule
based
Cost
based
SELECT * FROM EMP
WHERE ENAME LIKE 'AB%'
AND EMPNO = '7890'
SELECT * FROM EMP
WHERE ENAME LIKE 'AB%'
AND JOB LIKE 'SA%'
SELECT * FROM EMP
WHERE JOB = 'SALESMAN'
AND EMPNO = '7890'
나중에
생성된
Index 사용
SELECT * FROM EMP
WHERE JOB = 'SALESMAN'
AND EMPNO = '7890'
분포도에 따라
ENAME index
도 사용
INDEX merge
(and_equel),
특정 index
분포도에 따라
index 사용,
혹은 full scan
항상 EMPNO
Index만 사용
INDEX merge
(and_equel)
15. 전체범위 VS 부분범위
전 체 범 위 처 리
2
차
가
공
운반단위
•
•
•
•
1
차
스
캔
Full Range Scan 후 가공하여
Array Size 만큼 추출
부 분 범 위 처 리
2
차
가
공
운반단위
1
차
스
캔
조건을 만족하는 Row 수가 Array
Size 에 도달되면 멈춤
18. SORT를 대신하는 INDEX 실습
회원 정보를 이름 + 생년월일 순으로 descending정렬해서
모든 데이터를 가져 오시오.
추가되어야 할 인덱스를 정의 하시고 SQL문을 작성하세요.
단, ORDER BY를 사용하지 말고 INDEX를 활용해서 하세요
인덱스 명 : 회원_PK (회원ID)
SELECT *
FROM 회원
19. 부분범위 처리 예제
SORT를 대신하는 INDEX
SELECT * FROM PRODUCT
WHERE YMD = ‘130321'
AND ITEM LIKE 'AB%'
ORDER BY YMD, ITEM
운반단위
전
체
스
캔
INDEX
(YMD)
TABLE
S
O
R
T
.
.
.
.
.
.
.
.
SELECT * FROM PRODUCT
WHERE YMD = ‘130321'
AND ITEM LIKE 'AB%'
운반단위
부
분
스
캔
INDEX
(YMD+ITEM)
TABLE
20. 부분범위 처리 예제
INDEX만 읽고 처리
SELECT DEPT, SUM(QTY)
FROM PRODUCT
WHERE DEPT LIKE '12%'
GROUP BY DEPT;
운반단위
INDEX
(DEPT)
TABLE
•
•
G
R
O
U
P
B
Y•
•
SELECT DEPT, SUM(QTY).
FROM PRODUCT
WHERE DEPT LIKE '12%'
GROUP BY DEPT;
운반단위
INDEX
(DEPT+QTY)
G
R
O
U
P
B
Y
•
•
•
•
21. 부분범위 처리 예제
INDEX만 읽고 처리(예제)
INDEX 정보 TF_ORD_I3 (ITEM, STATUS)
SQL> SELECT STATUS, COUNT(*)
FROM TF_ORDER
WHERE ITEM LIKE 'HJ%'
GROUP BY STATUS
20 SORT GROUP BY
36631 INDEX RANGE SCAN TF_ORD_I3
2.5 sec20 SORT GROUP BY
36630 TABLE ACCESS BY ROWID TF_ORDER
36631 INDEX RANGE SCAN TF_ORD_I3
SQL> SELECT TYPE, COUNT(*)
FROM TF_ORDER
WHERE ITEM LIKE 'HJ%'
GROUP BY TYPE
10.3 sec
22. 부분범위 처리 예제
MAX 처리
SELECT MAX(SEQ) + 1
FROM PRODUCT
WHERE DEPT = '12300';
운반단위
INDEX
(DEPT)
TABLE
•
•
S
O
R
T
•
•
MAX(SEQ)+1
SELECT /*+ INDEX_DESC( A INDEX1) */
SEQ + 1
FROM PRODUCT A
WHERE DEPT = '12300'
AND ROWNUM = 1;
운반단위
INDEX
(DEPT+SEQ)
SEQ + 1
23. 부분범위 처리 예제
ROWNUM의 활용
SELECT COUNT(*) INTO :CNT
FROM ITEM_TAB
WHERE DEPT = '101'
AND SEQ > 100
. . . . . . .
IF CNT > 0 . . .
. . . . . . .
INDEX
(DEPT)
TABLE
.
.
.
.
.
.....
운반
단위
COUNT
SELECT 1 INTO :CNT
FROM ITEM_TAB
WHERE DEPT = '101'
AND SEQ > 100
AND ROWNUM = 1
. . . . . . . .
IF CNT > 0
. . . . . . . .
INDEX
(DEPT)
TABLE
운반
단위
X
O
27. INDEX를 사용 못하게 되는 경우와 사례
INDEX COLUMN의 변형
SELECT *
FROM DEPT
WHERE SUBSTR(DNAME,1,3) = 'ABC'
NOT Operator
NULL, NOT NULL
Optimizer 의 취사선택
SELECT *
FROM EMP
WHERE JOB <> 'SALES'
SELECT *
FROM EMP
WHERE ENAME IS NOT NULL
SELECT *
FROM EMP
WHERE JOB LIKE 'AB%'
AND EMPNO = '7890'
Function Based Index 사용시는 예외
28. 다양한 인덱스 스캔 방식
어떤 골프채로 쳐야 할까요?
=
어떤 인덱스 스캔 방식을 써야 효율적일까요?
30. INDEX UNIQUE SCAN
Unique Scan
=인덱스의 모든 컬럼 매칭 한 건
Equal 데이터 한 건 추출
범위검색 조건(between, 부등호, like) 인 경우
Range Scan 으로 검색(한 건 이상 이기 때문)
(Unique Index 생성 시)
31. INDEX Range Scan / Descending
INDEX UNIQE SCAN INDEX RANGE SCAN
CREATE UNIQUE INDEX 인덱스명 CREATE INDEX 인덱스명
Unique Scan Range Scan
32. INDEX Range Scan / Descending
INDEX UNIQE SCAN INDEX RANGE SCAN
>
데이터 추출 시 인덱스 컬럼 순으로 정렬
ORDER BY , MIN, MAX 값을 대체 가능
선두 컬럼이 조건절에 사용 되지 않으면
INDEX FULL SCAN 발생
인덱스를 스캔하는 범위를 얼마만큼 줄이고
테이블 엑세스를 적게 하는게 속도향상 포인트
Range Scan
Descending
33. INDEX SKIP SCAN
인덱스 : 기준일자 + 업종코드
SELECT /*+INDEX(A 일별업종별거래_IDX)*/
기준일자, 업종코드, 체결건수, 체결수량, 거래대금
FROM 일별업종별거래 A
WHERE 기준일자 BETWEEN ‘20130701’ AND ‘20130709’
AND 업종코드 = ‘01’
INDEX RANGE SCAN
SELECT /*+INDEX(A 일별업종별거래_IDX)*/
기준일자, 업종코드, 체결건수, 체결수량, 거래대금
FROM 일별업종별거래 A
WHERE 업종코드 = ’01’
INDEX SKIP SCAN
Oracle 9i부터 사용가능
중복된 값이 많을 수록 유리
34. INDEX FULL SCAN
Full Scan
INDEX UNIQE SCAN INDEX RANGE SCAN
> >INDEX SKIP SCAN
>TABLE FULL SCAN
>INDEX FULL SCAN
<?>TABLE FULL SCAN INDEX FULL SCAN
(효율?)
SELECT * FROM EMP WHERE SAL > 5000
ORDER BY EMP_NAME
데이터 건이 많으면 Random Access발생으로
TABLE FULL SCAN을 이용 해야 효율적
데이터 량이 많아지면 I/O낭비가 심해짐
데이터 건이 극히 일부일 때만
TABLE FULL SCAN 보다 유리함.
INDEX와 마찬가지로 ORDER BY 사용 가능
대부분 인덱스가 차지하는
면적이 테이블보다 작음
36. INDEX FAST FULL SCAN
Index Full Scan 방식 : 1번 브랜치 > 1 > 2 > 3 > 4 > 5 > 6 > 7 > 8 > 9 > 10
Index Fast Full Scan 방식 : 1 >2>10>3>9>8>7>4>5>6 순서대로 전체를 읽음
37. INDEX FAST FULL SCAN
INDEX FULL SCAN INDEX FAST FULL SCAN
1.인덱스 구조를 따라 스캔 1.세그먼트 전체를 스캔
2.결과집합 순서 보장 2.결과집합 순서 보장 안 됨
3.Single Block I/O 3.Multiblock I/O(세그먼트 전체스캔)
4.병렬스캔 불가(파티션 돼 있지 않다면) 4.병렬스캔 가능
5.인덱스에 포함되지 않은 컬럼 조회시에도 사용가능 5.인덱스에 포함된 컬럼으로만 조회할 때 사용 가능
SELECT * FROM 공급업체
WHERE 업체명 LIKE ‘%네트웍스%’
SELECT /*+ORDERED USE_NL(B) NO_MERGE(B) ROWID(B)*/
B.*
FROM (
SELECT /*+INDEX_FFS(공급업체 공급업체_IDX)*/
ROWID AS RID
FROM 공급업체
WHERE INSTR(업체명,’네트웍스’) > 0
)A,
공급업체 B
WHERE B.ROWID = A.RID
10g 부터는 index range or full scan 일 때도 Multiblock I/O스캔발생 (인덱스만 읽을 때)
39. AND-EQUAL
SELECT CUSTNO, CHULDATE
FROM CHULGOT
WHERE CUSTNO = 'DN02'
AND STATUS = '90'
SELECT CUSTNO, CHULDATE
FROM CHULGOT
WHERE CUSTNO LIKE 'DN%'
AND RTRIM(STATUS) LIKE '9%'
TABLE ACCESS BY ROWID CHULGOT
AND-EQUAL
INDEX RANGE SCAN CH_STATUS
INDEX RANGE SCAN CH_CUSTNO
SELECT CUSTNO, CHULDATE
FROM CHULGOT
WHERE CUSTNO LIKE 'DN%'
AND STATUS LIKE '9%'
Oracle 10g부터 폐기된 기능입니다! 10g 이하버젼을 쓰시는 분만 참고하세요.
45. BITMAP
BITMAP INDEX
레코드 찾아가는 방법
Block안에 저장할 수 있는 최대 Record 개수 730개
테이블에 20개 BLOCK이 할당이 되어있다고 가정
730 X 20 = 14600 9500번째가 1라면
BLOCK 위치 : 9500 / 730 = 13
Record 위치 : MOD(9500 / 730) = 10
Statement, prestatement
바인딩 단점을 보완한 바인딩변수 peeking : 하드파싱 시 컬럼분포도를 이용해 통계정보를 만들어낸다
-SQL 파서가 파싱
반복사용하기 위해 라이브러리 캐쉬에 저장( 커서 공유)
-최적화하기 쉬운형태로 변환
후보군이 될만한 실행계획들 생성
오브젝트 통계정보, 시스템 성능 통계정보를 이용하여 필요한 I/O, CPU, 메모리 사용량 등을 예측
-SQL 실행계획 생성
Table Full Scan 보다 I/O를 줄일 수 있거나 정렬된 결과를 쉽게 얻을수 있다면 Index Full Scan방식 사용