SlideShare a Scribd company logo
1 of 212
Download to read offline
데이타베이스 튜닝교육
2011. 01. 24
“SQL 튜닝의 기본개념을 알아 보고 제품에서 활용하자”
전문가들이 하는 DB 튜닝방법은?
업무를 분석하고
데이타베이스 모델을 분석
테이블 및 인덱스를 확인하고
실행계획 및 트레이스를 분석하고
다른 업무에 영향력을 검토 후
최적의 index 및 쿼리 튜닝 작업을 진행
우리들의 작업과정은?
고객으로부터 요청을 받으면
통계화면이 느리다
는데.. 튜닝 한번
해봐~ 저 그런거 안해봤는
데 어떻게 하죠?
개발자
PM
음… 할 사람이 없
으니 우선 한번 해
봐봐 그럼 제가 한번 찾
아서 해볼께요
개발자
PM
어느것 부터 봐야할지 막막하다.
일단.. 실행되는 쿼리를 찾고
쿼리를 실행시켜본다.
풀스캔 타는 쿼리는
무조건 인덱스 추가
해
인덱스는 많이 만
들수록 좋아
DBMS버전, 컬럼순서, 업무영향도, 조인형태를 무시
하고
무조건 인덱스를 추가한다.
전보다 더 느려졌어요.
어떻게 된거예요?
더 느려졌자나..
똑바로 작업 안해?
그렇게 시간만 자꾸 흘러간다...
방법을 몰라서 시간이 없어서
Why?
SQL 튜닝을 잘하려면
SQL실행의 기본원
리를 공부하세요!!
누구나 SQL을
능숙하게 사용하
고 싶어한다.
업계 최고의 전문가는 아니지만...
현재보다 훨씬 좋아질 수 있다.
그럼 같이 한번
달려볼까요?
데이타베이스 튜닝 관련 서적
“기본개념 습득에 좋은 책”
I. SQL 실행계획
II. 인덱스(INDEX)
III. 조인 최적화
IV. 부분범위처리
목 차
V. MSSQL 쿼리
VI. eNomix에서 사용하는 쿼리튜닝
VII. 쿼리 작성 팁
VIII. 에필로그
작업 환경설정
오라클 클라이언트
kmhan튜닝실습자료Tools오라클클라이언트_10g
오라클 클라이언트 툴
kmhan튜닝실습자료ToolsSQLTools_pp
kmhan튜닝실습자료ToolsSQLDeveloper
PLAN_TABLE
kmhan튜닝실습자료PLAN_TABLE
DB 접속계정
eNomix5.4.2, EE가 설치된 접속계정 ­ MSSQL, ORACLE
I. SQL 실행계획
II. 인덱스(index)
III. 조인 최적화
IV. 부분범위처리
1.1 내부 실행과정
1.2 Optimizer
1.3 Optimizer에 영향을 미치는 요소
1.4 SQL 실행계획의 이해
1.1 내부 실행과정
SQ
L
OPTIMIZER
Select cols1,
col2, col3...
From account A,
customer B
Where A.col1 =
B.cols2
AND A.col2 = ‘abc’
SQL
해석
실행
계획
작성
실행
참조 참조 추출
- 사용자는 요구만 하고
Optimizer가 실행계획 수립
- 수립된 실행계획에 따라
엄청난 수행속도 차이 발생
- 실행계획 제어가 어렵다.
- Optimzer가 최적의 실행계획을
수립할 수 있도록 종합적이고
전략적인 Factor를 부여
- 비절차형으로 기술해야 함.
- 집합적으로 접근해야 함
- SQL이 어떤 역할을 담당하도록 구현
할 것인가?
결과
1.2 Optimizer
Optimizer
SQL로 요구된 결과를 최적의 성능을 보장하면서 최소의 비용으로 처리할 수 있도록
처리 경로를 결정하는 DBMS의 일부분
CBO vs RBO
1) CBO : 통계정보로부터모든 Access Path 고려하여 실행계획수립
2) RBO : 미리 정해진 Rule에 따라 실행계획수립
Optimizer Mode
1) RULE
2) CHOOSE
3) ALL_ROWS
4) FIRST_ROWS
1.2 Optimizer
RBO
- Rule Based Optimizer
- 여러 개의 실행계획 가운데 가장 높은 순위의 실행계획을 사용
- 수립된 실행계획은 예측 가능
- 분포도를 무시하므로 더 느려질 수 있다.
CBO
- Cost Based Optimizer
- 여러 개의 실행계획 가운데 가장 적은 비용을 가진 실행계획을 사용
- 비용산정 요소로는 SQL형태, Hint, Optimizer Mode, 연산자, Index, Cluster, DBMS용량 등등
- 성능을 최적으로 내기 위해서는 주기적으로 analyze object를 해주어야 한다.
1 Rowid
2 클러스터 조인
3 Unique를 사용한 해시 클러스터 키
4 Unique key
5 클러스터 조인
6 해시 클러스터 키
7 인덱스된 클러스터 키
8 복합컬럼 인덱스 키
9 단일컬럼 인덱스 키
10 범위검색 (Bounded)
11 무계검색 (Unbounded)
12 소트머지 조인
13 최대/최소 인덱스 컬럼
14 Order by 인덱스 컬럼
15 Full table scan
1.3 Optimizer에 영향을 미치는 요소
1.4 SQL 실행계획의 이해
SELECT A.qna_id, A.question_title
FROM t_qna A, t_qna_process B
WHERE A.qna_id = B.qna_id
AND A.spam_flag = 'N‘ AND A.delete_flag = 'N‘ AND A.created_date >= '20101001000000'
AND EXISTS (
SELECT 1
FROM t_qna_option
WHERE option01 = ‘01023070004'
);
ORACLE
- PLAN TABLE
생성
[UNIX]
SQL> @$ORACLE_HOME/rdbms/admin/utlxplan.sql
[WINDOWS]
SQL> @C:ORACLEORA9IRDBMSADMINUTLXPLAN.SQL
실행계획 만들기
EXPLAIN PLAN [SET statement_id = ‘description’]
[INTO table_name]
FOR sql_statement
오라클 실행계획을 볼려면 사용자 계정에 plan_table이 생성되어 있어야 한다.
- PLAN TABLE
연습
EXPLAIN PLAN SET statement_id = ‘KMHAN’
INTO plan_table
FOR SELECT * FROM t_qna
WHERE qna_id = ‘QNA’
확인
DBMS_XPLAN 패키지 이용
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY('PLAN_TABLE', 'KMHAN', 'BASIC'))
Oracle 9.1부터
지원
BASIC, TYPICAL, SERIAL ­ 실행계획 정보 표시
ALL ­ 실행계획의 모든 항목을 표시
OUTLINE ­ 실행계획을 수립하는데 필요한 힌트목록을 표시
ADVANCED ­ ALL + OUTLINE
[연습1] 다음 실행계획의 실행 순서를 말해 보시오.
SELECT STATEMENT GOAL CHOOSE
NESTED LOOPS
TABLE ACCESS FULL OF ‘T_QNA_PROCESS’
INDEX UNIQUE SCAN OF ‘PK_QNA’
0
1
2
3
EXECUTION PLAN
---------------------------------------------
2 è 3 è 1 è 0
안쪽부터
위에서 아래로
[연습2] 다음 실행계획의 실행 순서를 말해 보시오.
SELECT STATEMENT GOAL CHOOSE
SORT ORDER BY
FILTER
NESTED LOOPS
NESTED LOOPS
TABLE ACCESS BY INDEX ROWID OF ‘T_QNA’
INDEX RANGE SCAN OF ‘IDX_QNA_CREATED_DATE_SPAM_DEL’
TABLE ACCESS BY INDEX ROWID OF ‘T_NODE’
INDEX UNIQUE SCAN OF ‘PK_NODE’
INDEX RANGE SCAN OF ‘IDX_QNA_PROCESS_QNA_ID’
TABLLE ACCESS FULL OF ‘T_QNA_OPTION)
0
1
2
3
4
5
6
7
8
9
10
EXECUTION PLAN
---------------------------------------------
6 è 5 è 8 è 7 è 4 è 9 è 3 è 10 è 2 è 1 è 0
- AUTOTRACE
실행계획 + 실행통계 정보를 표시해줌
1. set autotrace on è sql을 실제 수행하고 그 결과와 함께 실행계획 및 실행 통계를 출력한다.
2. set autotrace on explain è sql을 실제 수행하고 그 결과와 함께 실행계획을 출력한다.
3. set autotrace on statistics è 실제 수행하고 그 결과와 함께 실행통계를 출력한다
4. set autotrace traceonly è sql을 실제 수행하지만 그 결과는 출력하지 않고 실행계획과 통
계만 출력한다.
5. set autotrace traceonly exlain è sql을 실제 수행하지 않고 실행계획만 출력한다.
6. set autotrace traceonly statistics è sql을 실제 수행하지만 그 결과는 출력하지 않고 실행
통계만을 출력한다.
plustrace 롤 : 실행통계를 확인하기 위해서는 v_$sesstat, v_$statname, v_$mystat 뷰를 읽을
권하이 필요
SQL>@?/sqlplus/admin/plustrace.sql
SQL>grant plustarce to 계정;
- TKPROF
트레이스 파일은 보기가 힘들다
è 트레이스 파일을 보기 좋게 포맷팅 해준다.
[트레이스 파일을 분석하는 유틸리티]
1) 실행된 SQL문
2) 구문분석 수행을 위한 CPU 사용시간
3) 디스크로부터 읽은 블록 수
4) 메모리로부터 읽은 블록 수
5) 조건을 만족하는 행 수
6) 옵티마이저의 유형
7) 분석된 실행 계획
8) 실행계획의 비용 계산 결과
- TKPROF
실행) tkprof trace파일경로 출력파일
tkprof /oracle/product/10.2.0/db_1/rdbms/log/testdb_ora_14108.trc report.prf
call count cpu elapsed disk query current rows
--------- --------- -------- ---------- ---------- ---------- ---------- ----------
Parse 12 0.00 0.00 0 0 0 0
Execute 203 0.04 0.08 0 0 0 0
Fetch 239 0.03 0.11 42 703 0 1433
--------- --------- -------- ---------- ---------- ---------- ---------- ----------
total 454 0.08 0.21 42 703 0 1433
SQL 실행계획의 이해
SELECT A.qna_id, A.question_title
FROM t_qna A, t_qna_process B
WHERE A.qna_id = B.qna_id
AND A.spam_flag = 'N‘ AND A.delete_flag = 'N‘ AND A.created_date >= '20101001000000'
AND EXISTS (
SELECT 1
FROM t_qna_option
WHERE option01 = ‘01023070004'
);
MSSQL
4. SQL 실행계획의 이해 (MSSQL)
MSSQL PLAN_TABLE?? è 필요 없다.
실행계획에 대해서 그래프로 표시해준
다.
4. SQL 실행계획의 이해 (MSSQL)
SET SHOWPLAN_TEXT ON
SET SHOWPLAN_ALL ON
SET STATISTICS PROFILE ON
SET STATISTICS IO ON
SET STATISTICS TIME ON
è 예상 실행계획 표시(StmtText)
è 예상 실행계획 표시(모든 정보)
è SQL을 실행할때 사용한 실행계획 표시
è 디스크를 읽은 양에 대해 표시
è 쿼리가 걸리는 시간에 대해 표시
I. SQL 실행계획
II. 인덱스(index)
III. 조인 최적화
IV. 부분범위처리
2.1 인덱스란?
2.2 인덱스의 ACCESS 유형
2.3 인덱스 활용성 방안
2.4 인덱스를 이용한 엑세스 효율 향상
2.5 인덱스의 전략적 구성과 유지
2.1 인덱스란?
인덱스(INDEX)의 특성
- INDEX는 하나 혹은 두개 이상의 컬럼과 ROWID로 구성.
- INDEX는 INDEX를 구성하는 컬럼 값으로 정렬되어 있으며 모든 값이 동일할 경우 Physical
Address (Oracle => ROWID) 로 정렬되어 있다.
- ROWID는 INDEX를 읽고서 테이블을 읽기 위한 몇 가지 정보로 구성되어 있다.
- INDEX는 테이블과는 물리적으로 다른 저장장소에 저장될 수 있다.
- INDEX가 존재한다고 해서 항상 INDEX를 사용해서 테이블을 액세스 하는 것은 아니다 -Optimizer가 사용여부
결정
- 일반적으로 INDEX는 B-Tree(Balanced Tree)구조를 채택하고 있다.
인덱스(INDEX)의 키(Keys)
- Index : DB 내에 실제로 저장되는 물리적 구조체. DDL로 생성,변경,삭제. 빠른 데이터 엑세스 지원.
- Key : 논리적 개념. DBMS에서 Integrity Constraint로 구현되고, Index에 의해서 보장되기도 함.
테이블 액세스(Access) 방법
- By User ROWID : 테이블의 특정 Row에 대한 위치정보(ROWID)를 가지고 직접 테이블 액세스
- Full Table Scan : 테이블에 존재하는 모든 Row에 대한 블록의 순차적액세스
- By Index ROWID : 먼저 INDEX를 읽고서 INDEX의 ROWID를 가지고 테이블 Random 액세스
- 인덱스의 구조 (B*TREE)
아이디 이름 직급
hgkim 김현곤 대리
jgpark 박정근 부장
kwlee 이경환 사원
hschoi 최학석 대리
ekpark 박은규 부장
shpark 박성후 차장
shnam 남석현 차장
hsyoon 윤현상 대리
ynkim 김용남 사원
yjpark 박유진 대리
SELECT 아이디, 이름, 직급
FROM 스펙트라_직원
WHERE 직급 = ‘대리’
INDEX(직급) TABLE(스펙트라_직원)
SORT된 결과
사원 0000A95B.0002.0001
사원 0000A95B.0003.0001
사원 0000A95B.0102.0001
대리 0000A95B.0402.0001
대리 0000A95C.0062.0001
대리 0000A91D.0002.0001
대리 0000A95E.0002.0001
과장 0000A95B.2002.0001
과장 0000A95B.0302.0001
차장 0000A95B.1002.0001
차장 0000A95B.0032.0001
INDEX-KEY ROWID
2.1 인덱스란?
2.2 인덱스의 ACCESS 유형
발생
- 아무런 조건 없이 Table을 읽게 한 경우
- 인덱스가 걸려 있지 않은 컬럼에 대해서 조건주고 Table을 읽게 한 경우
- 인덱스가 걸려 있는 컬럼에 조건을 부여 했을지라도 Optimizer가 Full Table Scan이 유
리하다고 판단한 경우
Access 방식
- 테이블의 첫Row가 들어있는 Block부터 HWM(High Water Mark)까지 읽는다
- 한번에 DB_FILE_MULTIBLOCK_READ_COUNT에서 정한 크기만큼 읽는다
※ HWM(High Water Mark) ­ 해당 테이블의 데이터가 쓰여진 적이 있는 가
장 마지막 Block
- FULL TABLE SCAN
- FULL TABLE SCAN 예제
SELECT STATEMENT CHOOSE
TABLE ACCESS (FULL) OF T_EMP
SELECT *
FROM t_emp
결과 14건
SELECT STATEMENT CHOOSE
TABLE ACCESS (FULL) OF T_EMP
SELECT *
FROM t_emp
WHERE deptno = 20
결과 5건
실제 읽은 데이터는 동일하다.
2.2 인덱스의 ACCESS 유형
발생
- Unique Index를 구성하고 있는 모든 Key값에 대해서 Equal(=)로 조건이 공급된 경
우 발생한다
Access 방식
- 해당 조건을 만족 하는 값 하나만 읽는다.
- INDEX UNIQUE SCAN
2.2 인덱스의 ACCESS 유형
SELECT STATEMENT CHOOSE
TABLE ACCESS BY INDEX ROWID OF T_EMP
INDEX (UNIQUE SCAN) OF PK_EMP (UNIQUE)
SELECT *
FROM t_emp
WHERE empno = 7782
결과 1건
- INDEX UNIQUE SCAN 예제
2.2 인덱스의 ACCESS 유형
발생
- Non-Unique Index를 Access 하는 경우
- Unique Index를 구성하고 있는 컬럼 중 일부 컬럼 에만 값이 공급된 경우
- Unique Index에 Range 조건(like, between, >, <, >=, <=)으로 값이 공급되는 경
우
Access 방식
- 해당 조건을 만족하는 범위+ 아닌 값 하나(1Plus Scan)를 읽게 된다.
- Range 조건이 들어온 경우 Index 구성 순서상 이후에 있는 컬럼에 공급된 조건들은 작
업범위를 줄이는데 작용하지 못한다.
※ 1 Plus Scan - 조건을 만족하는 것을 찾았을지라도 다음에 어떤 값이 오는지
모르기 때문에 조건을 만족하지 않는 값 한 건을 더 Access해야만 원하는 범위
를 다 알 수 있다고 판단하여 조건을 만족하지 않는 값
한 건을 더 Access하는 것을 표현한 용어.
- INDEX RANGE SCAN
2.2 인덱스의 ACCESS 유형
SELECT STATEMENT CHOOSE
TABLE ACCESS BY INDEX ROWID OF T_EMP
INDEX (RANGE SCAN) OF EMP_IDX01 (NON-UNIQUE)
SELECT *
FROM t_emp
WHERE mgr = 7839
SELECT STATEMENT CHOOSE
TABLE ACCESS BY INDEX ROWID OF T_EMP
INDEX (RANGE SCAN) OF PK_EMP (UNIQUE)
SELECT *
FROM t_emp
WHERE mgr = 7839
- INDEX RANGE SCAN 예제
2.2 인덱스의 ACCESS 유형
발생
- Optimizer가 Full Table Scan하고 Sort하는 것 보다는 Index Full Scan해 Sort 작업
을 따로 수행하지 않는 것이 유리하다고 판단 한 경우
Access 방식
- 해당 인덱스의 모든 Block을 한번에 한Block씩 순차적으로 읽어 내려간다.
(Single Block I/O)
- 정렬된 결과를 가져올 수 있다.
- INDEX FULL SCAN
2.2 인덱스의 ACCESS 유형
SELECT STATEMENT CHOOSE
TABLE ACCESS BY INDEX ROWID OF T_EMP
INDEX (FULL SCAN) OF EMP_IDX02 (NON-UNIQUE)
SELECT /*+ INDEX(A idx_emp_idx02) */
empno, ename, job, hiredate
FROM t_emp A
WHERE job = 'SALESMAN
- INDEX FULL SCAN 예제
2.2 인덱스의 ACCESS 유형
발생
- Where절이나 Select절에 사용된 컬럼이 모두 하나의 인덱스에 구성된 컬럼인 경우
- 결합Index의 경우 최소한한 Column이 NOT Null로 지정되어 있어야 한다
Access 방식
- 인덱스 Leaf Block을 한번에 DB_FILE_MULTIBLOCK_READ_COUNT에서 정한 크기
씩 끝까지 읽어 내려가며 결과값의 Sort가 보장되지 않는다.
- Parallel로 수행 가능하다
- Full Table Scan보다 읽어야 할 Block의 수가 적어 유리하다
- INDEX FAST FULL SCAN
2.2 인덱스의 ACCESS 유형
SELECT STATEMENT CHOOSE
INDEX (FAST FULL SCAN) OF EMP_IDX02 (NON-UNIQUE
SELECT /*+ index_ffs(A t_emp_idx02) */
empno, ename, job
FROM t_emp A
WHERE job = 'SALESMAN'
EMP_IDX02 :
empno +
ename + job
- INDEX FAST FULL SCAN 예제
2.2 인덱스의 ACCESS 유형
발생
- 결합인덱스 사용시 선행키 값 없이 인덱스를 사용하는 방법이다.
- CBO에서만 사용 가능하다.
Access 방식
- 선행칼럼의 인덱스를 조건으로 지정하지 않으면 해당 선행칼럼 조건을 내부적으로 나누
어서 실행된다.
- 선행칼럼의 분포도가 좋지 않을수록 빠른 속도를 보장한다.
- INDEX SKIP SCAN
2.2 인덱스의 ACCESS 유형
SELECT STATEMENT CHOOSE
INDEX (SKIP SCAN) OF EMP_IDX02 (NON-UNIQUE)
SELECT empno, ename, job
FROM t_emp
WHERE job = 'SALESMAN'
EMP_IDX02 :
empno + job
- INDEX SKIP SCAN 예제
2.2 인덱스의 ACCESS 유형
발생
- Rowid가 조건으로 공급된 경우
Access 방식
- Rowid를 이용해서 특정 Block의 특정Row를 찾아간다.
- 가장 빠른 Access 방식이다.
- ROWID
2.2 인덱스의 ACCESS 유형
SELECT STATEMENT CHOOSE
TABLE ACCESS BY INDEX ROWID OF T_EMP
SELECT *
FROM t_emp A
WHERE rowid = ‘0000A95B.0402.0001’
- ROWID 예제
2.2 인덱스의 ACCESS 유형
INDEX 컬럼의 변형
SELECT *
FROM DEPT
WHERE SUBSTR(DNAME,1,3) = 'ABC'
NOT Operator
SELECT *
FROM EMP
WHERE JOB <> 'SALES'
NULL, NOT NULL
SELECT *
FROM EMP
WHERE ENAME IS NOT NULL
Optimizer의 취사선택
SELECT *
FROM EMP
WHERE JOB LIKE 'AB%'
AND EMPNO = '7890'
2.3 인덱스의 활용성 방안
- 인덱스를 사용하지 않는 경우
NULL값은 0이거나 공백이다 ?
NULL은 0도 공백도 아니다.
è 값을 모른다. 정해지지 않았다는 의미
col = null, col = ‘’ 사용못한다.
NULL에 대해서 알아봅시다.
SELECT NULL + 1 FROM DUAL의 값은?
è NULL
- 어떤 값보다 크거나 작지 않다.
- 그러므로 어떤 값과 비교 자체가 안된다.
- NULL값과의 연산결과는 NULL이다.
- 중요한 정보는 NULL대신 DEFAULT처리를 한
다.
NULL에 대해서 알아봅시다.
ORACLE에서는 NULL값을 인덱스에 저장하지 않는다.
SELECT * FROM TABLE WHERE COL1 IS NULL
SELECT * FROM TABLE WHERE COL1 IS NOT
NULL
è 인덱스 사용 불가
MSSQL에서는 NULL값을 인덱스에 저장한다.
SELECT * FROM TABLE WHERE COL1 IS NULL
SELECT * FROM TABLE WHERE COL1 IS NOT
NULL
è 인덱스 사용가능
NULL의 정렬순서는 어떻게?
[ORACLE]
1) A, B, NULL 순으로
2) NULL, A, B 순으로
[MSSQL]
1) NULL, A, B 순으로
2) NULL, A, B 순으로
TAB1의 COL1에 A, B, NULL값이 있다면
1. SELECT COL1 FROM TAB1 ORDER BY COL1
2. SELECT COL1 FROM TAB1 GROUP BY COL1
결과는?
SELECT *
FROM EMP
WHERE ENAME LIKE 'ABC%'
SELECT *
FROM EMP
WHERE SUBSTR(ENAME,1,3) = 'ABC'
SELECT *
FROM EMP
WHERE SAL * 12 = 12000000
SELECT *
FROM EMP
WHERE TO_CHAR(HIREDATE,'YYMMDD')
= '20030101'
SELECT *
FROM EMP
WHERE NVL(COMM,0) < 100
SELECT *
FROM EMP
WHERE SAL = 12000000 / 12
SELECT *
FROM EMP
WHERE HIREDATE =
TO_DATE('20030101','YYMMDD')
?
2.3 인덱스의 활용성 방안
- 인덱스 컬럼의 변형(External)
SELECT *
FROM EMP
WHERE EMPNO BETWEEN 100 AND 200
AND JOB = 'CLERK'
SELECT *
FROM EMP
WHERE EMPNO BETWEEN 100 AND 200
AND NVL(JOB,'X') = 'CLERK'
SELECT *
FROM EMP
WHERE DEPTNO || JOB = '10SALESMAN'
SELECT *
FROM EMP
WHERE DEPTNO = '10'
AND JOB = 'SALSMAN'
2.3 인덱스의 활용성 방안
- 인덱스 컬럼의 변형(External)
TABLE ACCESS BY ROWID CHULGOT
AND-EQUAL
INDEX RANGE SCAN CH_STATUS
INDEX RANGE SCAN CH_CUSTNO
SELECT custno, chuldate
FROM chulgot
WHERE custno = ‘DN02’
AND status = ’90’
4 ROWS
0.51 SEC
TABLE ACCESS BY ROWID CHULGOT
INDEX RANGE SCAN CH_CUSTNO
SELECT custno, chuldate
FROM chulgot
WHERE custno = ‘DN02’
AND RTRIM(status) = ’90’
4 ROWS
0.03 SEC
SELECT custno, chuldate
FROM chulgot
WHERE custno LIKE ‘DN%’
AND status LIKE ’9%’
4 ROWS
0.51 SEC
TABLE ACCESS BY ROWID CHULGOT
INDEX RANGE SCAN CH_CUSTNO
SELECT custno, chuldate
FROM chulgot
WHERE custno LIKE ‘DN%’
AND RTRIM(status) = ’9%’
4 ROWS
0.03 SEC
TABLE ACCESS BY ROWID CHULGOT
INDEX RANGE SCAN CH_STATUS
2.3 인덱스의 활용성 방안
- Suppressing 예제
SELECT * FROM SAMPLET
WHERE CHA = 10
SELECT * FROM SAMPLET
WHERE TO_NUMBER(CHA) = 10
SELECT * FROM SAMPLET
WHERE NUM LIKE '200310%'
SELECT * FROM SAMPLET
WHERE TO_CHAR(NUM) LIKE '200310%'
SELECT * FROM SAMPLET
WHERE DAT = '01-JAN-2003'
SELECT * FROM SAMPLET
WHERE DAT = TO_DATE('01-JAN-2003')
CREATE TABLE SAMPLET
( CHA CHAR(10),
NUM NUMBER (12,3),
VAR VARCHAR2(20),
DAT DATE)
2.3 인덱스의 활용성 방안
- 인덱스 컬럼의 변형(Internal)
SELECT * FROM SAMPLET
WHERE VAR = 10
SELECT * FROM SAMPLET
WHERE TO_NUMBER(VAR) = 10
SELECT * FROM SAMPLET
WHERE NUM = CHA
SELECT * FROM SAMPLET
WHERE NUM = TO_NUMBER(CHA)
SELECT * FROM SAMPLET
WHERE DAT = CHA
SELECT * FROM SAMPLET
WHERE DAT = TO_DATE(CHA)
SELECT * FROM SAMPLET
WHERE DAT = '01-JAN-2003'
SELECT * FROM SAMPLET
WHERE DAT = TO_DATE('01-JAN-2003')
2.3 인덱스의 활용성 방안
- 인덱스 컬럼의 변형(Internal)
SORT AGGREGATE
TABLE ACCESS FULL CHULGOT
SELECT SUM(UNCOST)
FROM CHULGOT
WHERE STATUS = :V1 90이 넘어옴
SELECT SUM(UNCOST)
FROM CHULGOT
WHERE STATUS = :V2 ‘90’이 넘어옴
SORT AGGREGATE
TABLE ACCESS BY ROWID T_QNA
INDEX RANGE SCAN CH_STATUS
1 ROW
23.12 SEC
1 ROW
0.12 SEC
CHAR
타입
2.3 인덱스의 활용성 방안
- 인덱스 컬럼의 변형(Internal)
SELECT ’Not found' INTO :COL1
FROM DUAL
WHERE NOT EXISTS ( SELECT '' FROM EMP
WHERE EMPNO = '1234')
SELECT 'Not found !' INTO :COL1
FROM EMP
WHERE EMPNO <> '1234'
SELECT *
FROM EMP
WHERE ENAME LIKE ’천%'
AND JOB <> 'SALES'
SELECT *
FROM EMP a
WHERE a.ENAME LIKE ’천%'
AND NOT EXISTS ( SELECT '' FROM EMP b
WHERE a.Empno = b.Empno
AND b.JOB = 'SALES')
2.3 인덱스의 활용성 방안
- Not operator
SELECT *
FROM EMP
WHERE ENAME > ‘ ‘
SELECT *
FROM EMP
WHERE ENAME IS NOT NULL
SELECT *
FROM EMP
WHERE COMM IS NOT NULL
SELECT *
FROM EMP
WHERE COMM > 0
SELECT *
FROM EMP
WHERE DEPT_NO IS NULL
2.3 인덱스의 활용성 방안
- NULL & Not NULL
Ranking의 차이
SELECT *
FROM EMP
WHERE ENAME LIKE 'AB%'
AND EMPNO = '7890'
INDEX Merge 회피
SELECT *
FROM EMP
WHERE ENAME = 'ABC'
AND JOB = 'SAT'
Low Cost의 선택
SELECT *
FROM EMP
WHERE EMPNO > '10'
HINT에 의한 선택
SELECT /*+ INDEX(EMP JOB_IDX) */ *
FROM EMP
WHERE ENAME LIKE 'AB%'
AND JOB LIKE 'SA%'
empno
인덱스만 사용
- Ename 혹은
job index 중 하
나만 사용
- FULL Scan
- Index Merge
Full Table Scan
job
인덱스만 사용
2.3 인덱스의 활용성 방안
- Optimizer의 취사선택
2.3 인덱스의 활용성 방안
- Rule based vs Cost based
SELECT * FROM EMP
WHERE JOB = 'SALESMAN'
AND EMPNO = '7890'
SELECT * FROM EMP
WHERE ENAME LIKE 'AB%'
AND EMPNO = '7890'
SELECT * FROM EMP
WHERE ENAME BETWEEN '1101' AND '1210'
AND JOB LIKE 'SA%'
SELECT * FROM EMP
WHERE JOB = 'SALESMAN'
AND EMPNO = '7890'
SELECT * FROM EMP
WHERE ENAME LIKE 'AB%'
AND EMPNO = '7890'
SELECT * FROM EMP
WHERE ENAME LIKE 'AB%'
AND JOB LIKE 'SA%'
RBO
CBO
INDEX_MERGE
(AND_EQUAL)
항상 EMPNO
INDEX만 사용
항상 나중에 생성된
INDEX만 사용
INDEX_MERGE
OR 특정 INDEX
분포도에 따라
ENAME INDEX도 사용
분포도에 따라
INDEX 사용
- 6 블럭 이상의 테이블에 적용 (6블럭 이하는 연결고리만)
- 컬럼의 분포도가 10 ~ 15 % 이내인 경우 적용 (손익분기점)
- 분포도가 범위 이내더라도 절대량(약 5,000건 이상)이 많은 경우에는
단일 테이블 클러스터링을 검토할 것(throughput)
- 분포도가 범위 이상이더라도 부분범위처리를 목적으로 하는 경우에는 적용
- 인덱스만을 사용하여 요구를 해결하고자 하는 경우는 분포도가 나쁘더라도
적용할 수 있음(손익분기점)
분포도 = 1 / 컬럼값의 종류 X 100
= 컬럼값의 평균 로우수 / 테이블의 총 로우수 X 100
2.3 인덱스의 활용성 방안
- 인덱스의 활용(적용기준)
ABC 10
ABC 23
ABC 26
ABC 32
ABC 67
BCA 12
BCA 24
BCA 29
CBA 11
CBA 19
123 7
123 9
123 32
123 36
123 42
123 52
123 56
123 65
123 67
321 13
32 ABC
123
67 ABC
123
SELECT COL1, COL2
FROM TAB1
WHERE COL1 = ‘ABC’
AND COL2 = 123
INDEX(COL1) INDEX(COL2) TABLE(TAB1)
2.4 인덱스를 이용한 엑세스 효율 향상
- INDEX MERGE
ABC 111 10
ABC 112 23
ABC 113 26
ABC 123 32
ABC 123 67
BCA 111 12
BCA 112 24
BCA 123 29
CBA 111 11
CBA 112 19
32 ABC
123
67 ABC
123
SELECT COL1, COL2
FROM TAB1
WHERE COL1 = ‘ABC’
AND COL2 = 123
INDEX(COL1, COL2) TABLE(TAB1)
2.4 인덱스를 이용한 엑세스 효율 향상
- 결합 인덱스
COL1 COL2 ROWID
A 110 10
A 111 11
A 112 5
A 113 18
A 114 54
A 115 23
A 116 12
A 117 53
A 118 22
A 119 14
A 120 41
B 110 15
B 111 25
B 112 62
COL2 COL1 ROWID
110 A X
110 B X
110 C X
110 D X
111 A X
111 B X
111 C X
111 D X
112 A X
112 B X
112 C X
112 D X
113 A X
113 B X
SELECT * FROM TAB1
WHERE COL1 = ‘A’
AND COL2 = ‘112’
2.4 인덱스를 이용한 엑세스 효율 향상
- EQUAL이 결합인덱스에 미치는 영
향
결과는
동일하
다
COL1 COL2 ROWID
A 110 10
A 111 11
A 112 5
A 113 18
A 114 54
A 115 23
A 116 12
A 117 53
A 118 22
A 119 14
A 120 41
B 110 15
B 111 25
B 112 62
COL2 COL1 ROWID
110 A X
110 B X
111 A X
111 B X
111 C X
111 D X
112 A X
112 B X
112 C X
112 D X
113 A X
113 B X
113 C X
114 A X
SELECT * FROM TAB1
WHERE COL1 = ‘A’
AND COL2 BETWEEN ‘111’ AND ‘113’
2.4 인덱스를 이용한 엑세스 효율 향상
- EQUAL이 결합인덱스에 미치는 영향2
COL2 COL1 ROWID
110 D X
111 A X
111 B X
111 C X
111 D X
112 A X
112 B X
112 D X
113 A X
SELECT * FROM TAB1
WHERE COL1 = ‘A’
AND COL2 BETWEEN ‘111’ AND ‘112’
COL2 COL1 ROWID
110 D X
111 A X
111 B X
111 C X
111 D X
112 A X
112 B X
112 D X
113 A X
SELECT * FROM TAB1
WHERE COL1 = ‘A’
AND COL2 IN (‘112’, ‘111’)
TABLE ACCESS BY ROWID TAB1
INDEX RANGE SCAN INDEX1
CONCATENCATION
TABLE ACCESS BY ROWID TAB1
INDEX RANGE SCAN INDEX1
TABLE ACCESS BY ROWID TAB1
INDEX RANGE SCAN INDEX1
2.4 인덱스를 이용한 엑세스 효율 향상
- IN을 이용한 엑세스 효율 향상
수학적 의미
A * (B + C) =
(A * B) + (A * C)
A and (B or C) =
(A and B) or (A and C)
A = and B in ( ‘1’, ‘3’ ) =
(A = and B = ‘1’) or (A = and B = ‘3’)
기하하적 의미
A (선분) G
--------------------- COL BETWEEN ‘A’ AND ‘G’
COL IN (‘A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘F’, ‘G’)
A B C D E F G
2.4 인덱스를 이용한 엑세스 효율 향상
- IN 연산자의 특성
SELECT
……………………
FROM TAB1
WHERE
AND
AND
AND
AND
COL4 > 1000
COL5 = ‘OPER’
COL1 = ‘ABC’
COL2 LIKE ’12%’
COL3 = ‘1234’
COL1 + COL2 + COL3
로 구성된 인덱스 사용 가정
주류
비주류(여당내 야당)
여당
야당
효율화란 ?
- 능력 있는 야당을 어떻게 여당으로 영입할 것인가? (인덱스 전략)
- 당내 비주류를 어떻게 주류로 끌어들일 것인가? (IN을 활용)
COL1 + COL2 + COL3
로 구성된 인덱스 사용 가정
2.4 인덱스를 이용한 엑세스 효율 향상
- SQL내에서 조건들의 역할
SELECT ……………………
FROM TAB1
WHERE 상품 = ‘PRINTER’
AND
AND
AND 판매일자 LIKE
‘201001%’
처리구분 BETWEEN
:VAL1 AND :VAL2
COL1 + COL2 + COL3
로 구성된 인덱스 사용 가정
- 상품 + 처리구분 + 판매일자 인덱
스를 사용한다고 가정
- 처리구분은 1, 2, 3, 4로 가정
COL1 + COL2 + COL3
로 구성된 인덱스 사용 가정
- =로 사용되지 않았으므로 판매일
자 조건은 비주류가 됨
- 사용자가 어떤 조건을 부여할지 알
수 없음
SELECT *
FROM TAB1
WHERE 상품 = ‘PRINTER’
AND 처리구분 IN (:VAL1, :VAL2, :VAL3, :VAL4)
AND 판매일자 LIKE ‘201001%’
2.4 인덱스를 이용한 엑세스 효율 향상
- IN을 활용한 실행계획 개선방법
SELECT ……………………
FROM TAB1
WHERE 상품 = ‘PRINTER’
AND
AND 판매일자 LIKE
‘201001%’
부서 LIKE :VAL1 || ‘%’
COL1 + COL2 + COL3
로 구성된 인덱스 사용 가정
상품 + 부서 + 판매일자
인덱스를 사용한다고 가정
SELECT *
FROM TAB1
WHERE 상품 = ‘PRINTER’
AND 부서 IN (SELECT 부서
FROM DEPT
WHERE 부서 LIKE VAL1)
AND 판매일자 LIKE ‘201001%’
현존하는 테이블을 이용하는 방법
NESTED LOOPS
VIEW
SORT(UNIQUE)
INDEX (RANGE SCAN) OF ‘부서_PK’
TABLE ACCESS (BY ROWID) OF ‘TAB1’
INDEX (RANGE SCAN) OF ‘INDEX1’
- 먼저 서브쿼리가 수행되어 N개의 =을 제공
- 즉, 상품=, 부서=, 판매일자 LIKE 범위가 서브쿼
리 결과만큼 수행됨
- 서브쿼리로 인한 결합처리 실행계획은 이와 같은
형태로 나타남
- 반드시 버스쿼리가 제공자가 될 것
2.4 인덱스를 이용한 엑세스 효율 향상
- 서브쿼리를 이용한 IN조건 추가
서브쿼리, 인라인뷰, 스칼라 서브쿼리?
인라인 뷰 - FROM절 뒤에 오는 서브쿼리
SELECT * FROM TAB1, (
SELECT * FROM TAB2
) B WHERE A.col1 = B.col1
서브쿼리? 인라인 뷰? 스칼라 서브쿼리?
서브쿼리 - 쿼리 안에 또 다른 SELECT 쿼리블럭
SELECT * FROM TAB1
WHERE col1 IN (SELECT * FROM TAB2)
스칼라 서브쿼리 - SELECT LIST의 서브쿼리
SELECT A.*,
(SELECT col2 FROM tab2 WHERE col2 =
A.col1)
FROM TAB1 A
모조(DUMMY) 테이블을 이용하는 방법
YMD YMD_DATE
20100101 01-JAN-2010
20100102 02-JAN-2010
……………… ………………
20110101 01-JAN-2011
……………… ………………
20291231 31-DEC-2029
YMD_DUAL
YMD YMD_DATE
201001 JAN-2010
201002 FEB-2010
……………… ………………
201101 JAN-2011
……………… ………………
202912 DEC-2029
YM_DUAL
NO NO2
1 01
2 02
……… ………
10 10
……… ………
99 99
COPY_T
- 각테이블의 컬럼마다 UNIQUE 인덱스를 생성해 둘 것
- YMD_DUAL은일자기간을 점으로 만들어 주기 위해 사용
- YM_DUAL은 월별 기간을 점으로 만들어 주기 위해 사용
- COPY_T는 데이터 복제나임의의 값을 생성해 주기 위해 사용
2.4 인덱스를 이용한 엑세스 효율 향상
- 서브쿼리를 이용한 IN 조건 추가
SELECT *
FROM TAB1
WHERE 상품 = ‘PRINTER’
AND 판매일자 IN (SELECT ymd
FROM ymd_dual
WHERE ymd BETWEEN ‘20110101
AND ‘20110110’
AND 부서 LIKE :VAL || ‘%’
SELECT *
FROM TAB1
WHERE 상품 = ‘PRINTER’
AND 판매일자 BETWEEN ‘20110101’
AND ‘20110110’
AND 부서 LIKE :VAL1 || ‘%’
SELECT *
FROM TAB1
WHERE 상품 = ‘PRINTER’
AND 구분 LIKE :type || ‘%’
AND 생산일자 = ‘20110115’
SELECT *
FROM TAB1
WHERE 상품 = ‘PRINTER’
AND 구분IN (
SELECT :type || NO
FROM copy_t
WHERE NO <= DECODE(:type, ‘A’, 10, 15))
AND 생산일자 = ‘20110115’
상품 + 판매일자 + 부서
인덱스를 사용한다고 가정
상품 + 구분 + 생산일자
인덱스를 사용한다고 가정
구분은 A01, …A10, B01, ...B15…
모조(DUMMY) 테이블을 이용하는 방법
2.4 인덱스를 이용한 엑세스 효율 향상
- 서브쿼리를 이용한 IN 조건 추가
1. 항상 사용하는가?
2. 항상 ‘=‘로 사용되는가?
3. 분포도가 좋은 컬럼 우선
4. SORT 순서는?
5. 어떤 컬럼을 추가? (후보선수 : 예방, 도움)
2.5 인덱스의 전략적 구성과 유지
- 결합인덱스 컬럼 순서 결정
2.5 인덱스의 전략적 구성과 유지
- INDEX 선정 기준
- 분포도가 좋은 컬럼은 단독적으로 생성하여 활용도 향상
- 자주 조합되어 사용되는 경우는 결합인덱스 생성
- 각종 액세스 경우의 수를 만족할수 있도록 인덱스간의 역할분담
- 가능한 수정이 빈번하지 않는 컬럼
- 기본키 및 외부키 (조인의 연결고리가 되는 컬럼)
- 결합 인덱스의 컬럼 순서선정에 주의
- 반복수행(loop 내) 되는 조건은 가장 빠른 수행속도를 내게 할 것
- 실제 조사된 액세스 종류를 토대로 선정 및 검증
2.5 인덱스의 전략적 구성과 유지
- INDEX 선정 시 고려사항
- 새로 추가된 인덱스는 기존 액세스 경로에 영향을 미칠 수 있음
- 지나치게 많은 인덱스는 오버헤드를 발생
- 넓은 범위를 인덱스로 처리시 많은 오버헤드 발생
- 옵티마이져를 위한 통계데이타를 주기적으로 갱신
- 인덱스의 개수는 테이블의 사용형태에 따라 적절히 생성
- 분포도가 양호한 컬럼도 처리범위에 따라 분포도가 나빠질 수 있음
- 인덱스 사용원칙을 준수해야 인덱스가 사용되어짐
- 조인(join)시에 인덱스가 사용여부에 주의
2.6 Hint
- Hint란?
Optimizer가 실행계획을 세워서 쿼리를 실행할 때 모든 것을 Optimizer에게 맡기지 않고 사용자가 원하는
보다 좋은 액세스 경로를 선택할 수 있도록 하는 것이다.
- 힌트의 사용법
--+ : 한 라인에 기술할 때
/*+ */ : 여러 라인에 걸쳐 기술할 때
- 사용예제
SELECT * FROM emp WHERE dept_name = ‘개발팀’
è SELECT /*+ INDEX(emp index_dept) */ * FROM emp WHERE dept_name = ‘개발팀’
사용하려는 인덱스가 없다면?
è 힌트를 무시
ORACLE
2.6 Hint
Oracle Hint의 종류에 대해서 알아보자
è 자세히 보기 (오라클 사이트)
액세스 수단 선택을 위한 힌트
힌트 내에 정의 된 테이블을 전체 테이블 스캔
FULL
인덱스 범위 스캔에 의한 테이블 액세스를 유도하는 힌트이다.
INDEX
인덱스를 경유하여 테이블 액세스할 때 인덱스 컬럼의 내림차순으로 범위 스캔하도록 유도
INDEX_DESC
2.6 Hint
쿼리형태 변형을 위한 힌트
조건절에 OR 혹은 IN연산자 조건을 별도의 실행단위로 분리하여 각각의 최적의 엑세스 경로를
수립
USE_CONCAT
조건절에 있는 OR 혹은 IN 연산자를 연결실행계획으로 처리되지 않도록 사용
USE_CONCAT의 반대개념
NO_EXPAND
서브쿼리와 메인쿼리를 합쳐 조인 형태로 변형하도록 하는 실행계획을 생성하도록 유도
UNNEST
서브쿼리와 메인쿼리를 합쳐 조인 형태로 변형하지 않도록 하는 실행계획을 생성하도록 유도
NO_UNNEST
2.6 Hint
조인 순서 조정을 위한 힌트
FROM절에 기술된 테이블 순서대로 조인하도록 유도
순서만 제시할 뿐 조인 방법과는 무관하므로 USE_NL, USE_MERGE와 같이 사용하는게 일반적
ORDERED
조인 순서를 제어하는 힌트
LEADING
2.6 Hint
조인 방법 조정을 위한 힌트
Nested Loops 방식을 사용하여 조인을 수행하도록 유도하는 힌트이다.
USE_NL
해쉬조인 방식으로 조인이 수행되도록 유도하는 힌트
USE_HASH
Sort Merge 방식으로 조인이 수행되도록 유도하는 힌트
USE_MERGE
2.6 Hint
- 힌트를 사용하는 위치
1. Table 뒤 With를 이용하는 방법
2. JOIN 구문 사이
3. 문장의 마지막 OPTION을 이용하는 방법
- 사용예제
SELECT COL1, COL2
FROM TAB1 A WITH (IDX_TAB1)
INNER HASH JOIN TAB2 WITH (NOLOCK)
WHERE A.COL3 = ‘1’
OPTION (FORCE ORDER)
사용하려는 인덱스가 없다면?
è 오류를 발생
MSSQL
테이블힌
트
조인힌트
쿼리힌트
MSSQL2005에서는
WITH (INDEX(IDX_TAB1))
으로 사용
2.6 Hint
MSSQL Hint의 종류에 대해서 알아보자
è 자세히 보기 (MS 사이트)
- 조인 힌트
- 쿼리 힌트
- 테이블 힌트
[실습2.1] 인덱스 엑세스 유형
아래 쿼리의 실행계획을 해석하라.
SELECT *
FROM t_qna
FULL TABLE SCAN
SELECT *
FROM t_qna
ORDER BY qna_id
INDEX UNIQUE SCAN
SELECT *
FROM t_qna
WHERE delete_flag = 'N'
AND created_date >= '20100101000000'
AND created_date <= '20110101000000';
INDEX RANGE SCAN
[실습2.2] 인덱스 엑세스 유형
아래 쿼리의 실행계획을 해석하라.
SELECT /*+ INDEX(A IDX_QNA_ACCOUNT_ANSWER_STATUS) */ account_id, qna_id
FROM t_qna A
WHERE answer_status = 'ANNOT'
AND approval_status = 'APNOT'
ORDER BY account_id
INDEX FULL SCAN
SELECT /*+ INDEX_FFS(A IDX_QNA_ACCOUNT_ANSWER_STATUS) */ account_id
FROM t_qna A
WHERE answer_status = 'ANNOT'
AND approval_status = 'APNOT'
ORDER BY account_id
INDEX FAST FULL SCAN
[실습2.3] 인덱스를 사용 못하는 구조
아래 쿼리에서 인덱스를 사용하는 쿼리로 바꿔라
SELECT *
FROM t_qna
WHERE domain_id = 'NODE0000000001'
AND SUBSTR(answer_status, 0, 3) = 'ANE'
SELECT *
FROM t_qna
WHERE domain_id = 'NODE0000000001'
AND answer_status LIKE 'ANE%'
SELECT *
FROM t_account
WHERE role_id <> 'ACCTAGT'
SELECT *
FROM t_account
WHERE role_id IN ('ACCTADM', 'ACCTMGR')
[실습2.4] 인덱스를 사용 못하는 구조
SELECT *
FROM t_node
WHERE alias = 2
SELECT *
FROM t_node
WHERE alias = ‘2’
아래 쿼리를 인덱스를 사용하는 쿼리로 바꿔라
데이타타입
[실습2.5] 인덱스를 사용 못하는 구조
SELECT 'Not Found' INTO :col1
FROM t_qna
WHERE customer_id <> 'rudaks'
SELECT 'Not Found' INTO :col1
FROM dual
WHERE NOT EXISTS (
SELECT 1 FROM t_qna
WHERE customer_id = 'rudaks‘)
SELECT *
FROM t_qna
WHERE node_id = 'NODE0000000001'
AND answer_status <> 'ANEND'
SELECT *
FROM t_qna A
WHERE node_id = 'NODE0000000001'
AND NOT EXISTS (
SELECT 1
FROM t_qna B
WHERE B.qna_id = A.qna_id
AND B.ANSWER_STATUS = ANEND' )
아래 쿼리를 인덱스를 사용하는 쿼리로 바꿔라
[실습2.6] 인덱스를 사용 못하는 구조
SELECT *
FROM t_qna
WHERE delete_flag = 'N'
AND answer_date IS NOT NULL
SELECT *
FROM t_qna
WHERE delete_flag = 'N'
AND answer_date > ‘ ‘
SELECT *
FROM t_qna
WHERE NVL(total_feedback, 0) > 50
SELECT *
FROM t_qna
WHERE total_feedback > 50
아래 쿼리를 인덱스를 사용하는 쿼리로 바꿔라
I. SQL 실행계획
II. 인덱스(index)
III. 조인 최적화
IV. 부분범위처리
3.1 조인의 개요
3.2 조인의 방법
3.3 Nested Loops Join
3.4 Sort Merge Join
3.5 Hash Join
3.6 Semi Join
3.7 Anti Join
3.8 조인방법 결정 및 성능 개선 팩터
3.9 조인방법의 결정
3.1 조인의 개요
SELECT col1, col2
FROM tab1, tab2
TAB1 COL1 TAB2 COL2
A 가
B 나
C 다
D
col1 col2
A 가
B 가
C 가
D 가
A 나
. .
. .
D 라
조인이란?
한 개 이상의 테이블에서 조건(where)에 해당 되는 데이터를 뽑아내는 것을 말한다.
결과는?
Nested Loops Join
Sort Merge Join
Hash Join
기타
- Semi Join
- Anti Join
3.2 조인의 방법
X
운
반
단
위
FLD1
= ‘AB’
KEY2
=
KEY1
FLD2
= ‘10’
TABLE
ACCESS
BY
ROWID
TABLE
ACCESS
BY
ROWID
INDEX
(FLD1)
TAB1 INDEX
(KEY2)
TAB2
SELECT A.FLD1, … B.FLD1
FROM TAB1 A, TAB2 B
WHERE A.KEY1 = B.KEY2
AND A.FLD1 = ‘AB’
AND B.FLD2 = ’10’
- 순차적(부분범위처리 가능)
- 종속적 (먼저 처리되는 테이블
의 처리범위에 따라 처리랑 결
정)
- 랜덤(Random) 액세스 위주
- 연결고리 상태에 따라 영향이
큼
- 주로 좁은 범위 처리에 유지
3.3 Nested Loops Join
- 개요
TABLE1 TABLE2 TABLE3
1 A
2 B
3 D
4 K
5 M
6 F
7 E
8 M
....
....
A 가
P 나
C 라
H 사
....
E 마
라 10
마 20
(10000
row)
(1000 row)
(2 row)
TABLE3 TABLE2 TABLE1
1 A
2 C
3 D
4 K
5 M
6 F
7 E
8 M
....
....
A 가
P 나
C 라
S 마
....
E 마
라 10
마 20
(10000
row)
(1000 row)
(2 row)
최소 10,000회 이상 ACCESS 최대 6회 이하 ACCESS
3.3 Nested Loops Join
- 조인성능결정(1) : Driving
TABLE1 TABLE2 TABLE3
1 A
2 B
3 D
4 K
5 M
6 F
7 E
8 M
....
....
A 가
P 나
C 라
H 사
....
E 마
라 10
마 20
(10000
row)
(1000 row)
(2 row)
TABLE1 TABLE3 TABLE2
- TABLE1과 두 번째 TABLE2(TABLE3)를 연결하는 일량은 성공한 결과에 관계없이 동
일
- 그러나 연결에 성공한 양은 다음 연결할 일량에 영향을 미침
1 A
2 C
3 D
4 K
5 M
6 F
7 E
8 M
....
....
A 가
P 나
C 라
S 마
....
E 마
C 10
E 20
(10000
row)
(1000 row)
(2 row)
3.3 Nested Loops Join
- 조인성능결정(2) : 조인순서
3.3 Nested Loops Join
- 조인성능결정(3) : INDEX의 영향
TAB1 TAB2
FLD KEY1
……… A
……… B
……… C
……… D
KEY2 FLD
C ………
A ………
G ………
P ………
①
②
인덱스 있음 인덱스 있음
TAB1 : INDEX SCAN
TAB2 : INDEX SCAN
TAB2 : INDEX SCAN
TAB1 : INDEX SCAN
①
②
TAB1 TAB2
FLD KEY1
……… A
……… B
……… C
……… D
KEY2 FLD
C ………
A ………
G ………
P ………
③
④
인덱스 있음 인덱스 없음
TAB1 : INDEX SCAN
TAB2 : FULL SCAN
TAB2 : FULL SCAN
TAB1 : INDEX SCAN
③
④
- 3의 경우 FULL SCAN이 TAB1의 대상 ROW마다 한번씩 실행이 되며,
옵티마이저는 무조건 4의 방법으로 처리가 된다.
- 양쪽 모두 인덱스가 없을 경우 SORT_MERGE나 HASH_JOIN으로 처리가 됨
SELECT /*+ ORDERED USE_NL(S C P) */
c.channel_desc, p.prod_name, s.*
FROM sales_t1 s, channels c, products p
WHERE s.channel_id = c.channel_id
AND s.prod_id = p.prod_id
AND c.channel_class = ‘Indirect’
AND p.prod_category = ‘Boys’
16467 NESTED LOOPS
297852 NESTED LOOPS
689122 TABLE ACCESS FULL SALES_T1
297852 TABLE ACCESS BY INDEX ROWID CHANNELS
689122 INDEX UNIQUE SCAN CHAN_PK
16469 TABLE ACCESS BY INDEX ROWID PRODUCTS
297852 INDEX UNIQUE SCAN PRODUCTS_PK
16.3 sec
SALES CHANNELS PRODUCTS
SELECT /*+ ORDERED USE_NL(S P C) */
c.channel_desc, p.prod_name, s.*
FROM sales_t1 s, products p , channels c
WHERE s.channel_id = c.channel_id
AND s.prod_id = p.prod_id
AND c.channel_class = ‘Indirect’
AND p.prod_category = ‘Boys’
16467 NESTED LOOPS
40008 NESTED LOOPS
689122 TABLE ACCESS FULL SALES_T1
40008 TABLE ACCESS BY INDEX ROWID PRODUCTS
689122 INDEX UNIQUE SCAN CHAN_PK
16469 TABLE ACCESS BY INDEX ROWID PRODUCTS
40008 INDEX UNIQUE SCAN PRODUCTS_PK
10.5 sec
SALES PRODUCTS CHANNELS
3.3 Nested Loops Join
- 조인 순서에 따른 수행속도 차이 예제
SELECT /*+ ORDERED USE_NL(Y X) */
CHULNO, CHULDATE, CUSTNAME
FROM CUSTOMER Y, CHULGOT X
WHERE X.CUSTNO = Y.CUSTNO
AND X.CHULDATE = ‘941003’
AND Y.NATION = ‘KOR’
NESTED LOOPS
TABLE ACCESS BY ROWID CUSTOMER
INDEX RANGE SCAN CU_NATION
TABLE ACCESS BY ROWID CHULGOT
INDEX RANGE SCAN CH_CUSTNO
1 rows
0.15 sec
SELECT /*+ ORDERED USE_NL(X Y) */
CHULNO, CHULDATE, CUSTNAME
FROM CHULGOT X, CUSTOMER Y
WHERE X.CUSTNO = Y.CUSTNO
AND X.CHULDATE = ‘941003’
AND Y.NATION = ‘KOR’
NESTED LOOPS
TABLE ACCESS BY ROWID CHULGOT
INDEX RANGE SCAN CU_CHULDATE
TABLE ACCESS BY ROWID CUSTOMER
INDEX RANGE SCAN PK_CUSTNO
1 rows
0.04 sec
3.3 Nested Loops Join
- 실행계획에 따른 수행속도 차이 예
제
SELECT /*+ ORDERED USE_NL(Y X) */
CHULNO, CHULDATE, CUSTNAME
FROM CUSTOMER Y, CHULGOT X
WHERE X.CUSTNO = Y.CUSTNO
AND X.CHULDATE = ‘941003’
AND Y.NATION = ‘KOR’
NESTED LOOPS
TABLE ACCESS BY ROWID CUSTOMER
INDEX RANGE SCAN CU_NATION
TABLE ACCESS BY ROWID CHULGOT
INDEX RANGE SCAN CH_CUSTNO
1 rows
0.15 sec
SELECT CHULNO, CHULDATE,
CUSTNAME
FROM CUSTOMER Y, CHULGOT X
WHERE RTRIM(X.CUSTNO) = Y.CUSTNO
AND X.CHULDATE = ‘941003’
AND Y.NATION = ‘KOR’
NESTED LOOPS
TABLE ACCESS BY ROWID CHULGOT
INDEX RANGE SCAN CH_CHULDATE
TABLE ACCESS BY ROWID CUSTOMER
INDEX RANGE SCAN CH_CUSTNO
1 rows
0.04 sec
SELECT CHULNO, CHULDATE, CUSTNAME
FROM CUSTOMER Y, CHULGOT X
WHERE RTRIM(X.CUSTNO) =
RTRIM(Y.CUSTNO)
AND X.CHULDATE = ‘941003’
AND Y.NATION = ‘KOR’
rows
sec
3.3 Nested Loops Join
- Join시 Index의 영향 예제
SELECT A1, A2, .., B1, B2, .., C1, C3, ..
FROM TAB1 x, TAB2 y, TAB3 Z
WHERE x.A1 = y.B1
AND z.C1 = y.B2
AND x.A2 = ’10’
AND y.B2 LIKE ‘B%’
TAB1
TAB2
TAB3
A2 = ’10’
B1 = A1 AND B2 LIKE ‘B%’
C1 = B2
TAB2
TAB3
TAB1
B2 LIKE ‘B%’
C1 = B2
A1 = B1 AND A2 = ’10’
TAB3
TAB2
TAB1
FULL TABLE SCAN
B2 = C1 AND B2 LIKE ‘B%’
A1 = B1 AND A2 = ’10’
...... ........................................
A2
A1
A2
B1
&B
2
C1
TAB2
# B1 #
B2
TAB2
# A1
A2
TAB3
# C1
C2
- 상수 값을 받을 수 있어야 액세스 자격이 획득됨(Nested Loops 조인인 경우)
- 어떤 액세스 경로가 가장 유리한가?
- 인덱스 구조가 어떻게 되어 있는가?
TAB1 TAB2 TAB3
3.3 Nested Loops Join
- 엑세스 경로의 결정
운
반
단
위
FLD1
= ‘AB’
KEY2
=
KEY1
TABLE
ACCESS
BY
ROWID
TABLE
ACCESS
BY
ROWID
INDEX
(FLD1)
TAB1 INDEX
(KEY2)
TAB2
SELECT A.FLD1, … B.FLD1
FROM TAB1 A, TAB2 B
WHERE A.KEY1 = B.KEY2
AND A.FLD1 = ‘AB’
AND B.FLD2 = ’10’
b.FLD2 = ’10’
인 CHECK기능
만 없어짐
b.FLD2 = ’10’이
없어도
일(엑세스)의 량에
별로 영향을 주지 않
음
3.3 Nested Loops Join
- Nested Loops Join의 특징
- 순차적으로 실행된다. 선행테이블(Driving Table)의 처리범위에 있는 각각의 로우들이 순차
적으로 수행(순차적)
- 먼저 액세스 되는 테이블의 처리범위에 의해 처리량이 결정된다.
- 나중에 처리되는 테이블은 앞서 처리된 값을 받아 액세스 된다. 자신에게 주어진 상수값만으
로 범위를 줄이는 것이 아니라 이미 가지고 있던 상수값과 제공받은 상수값을 합쳐서 그 중 가
장 유리한 방법으로 연결이 진행된다.
- 주로 랜덤 액세스 방식으로 처리된다.
- 주어진 조건에 있는 모든 컬럼들이 인덱스를 가지고 있더라도 모두가 사용되는 것은 아니다.
- 연결고리가 되는 인덱스에 의해 연결작업이 수행되므로 연결고리 상태가 매우 중요하다.
- 부분범위 처리가 가능하다.
- 연결작업을 수행한 후 마지막으로 체크되는 조건은 경우에 따라 수행속도에 미치는 영향이
달라진다.
3.3 Nested Loops Join
- Nested Loops Join의 특징
FLD1
= ‘AB’
FLD2
= ’10’
TABLE
ACCESS
BY
ROWID
TABLE
ACCESS
BY
ROWID
INDEX
(FLD1)
TAB1 TAB2
SELECT /*+ USE_MERGE(A B) */
A.FLD1, … B.FLD1
FROM TAB1 A, TAB2 B
WHERE A.KEY1 = B.KEY2
AND A.FLD1 = ‘AB’
AND B.FLD2 = ’10’
- 동시적(항상 전체범위 처리)
- 독립적 (자기의 처리범위 만
으로 처리량 결정)
- 스캔(SCAN)액세스 위주
- 연결고리 상태에 영향 없음
- 주로 넓은 범위 처리에 유리
S
O
R
T
S
O
R
T
S
O
R
T
INDEX
(FLD2)
운반단위
A.KEY1
= B.KEY2
를 조건으
로
MERGE
3.4 Sort Merge Join
- 개요
두개 이상의 조인 조건이 있으면서 하나 이상의 부등호 조인 조건을 갖는 경우
조인조건은 조인주관조건과 조인체크조건으로 나누어 진다.
Join을 위한 Sort Key 컬럼의 정렬순서는 조건 절에 기술된 순서대로 위에서 아래로 순서가 정해짐
SELECT /*+ ORDERED USE_MERGE(T1 T2) */
T1.NO, T1.NO_CH, T2.NO, T2.NO_CH
FROM COPY_T1 T1, COPY_T2 T2
WHERE T1.NO = T2.NO
AND T1.NO_CH < T2.NO_CH
MERGE JOIN
SORT JOIN
TABLE ACCESS FULL COPY_T1
FILTER
SORT JOIN
TABLE ACCESS FULL COPY_T2
- 조인주관조건 : T1.NO = T2.NO
- 조인체크조건 : T1.NO_CH < T2.NO_CH
- 정렬은 T1.NO, T2.NO로 각각 Ascending Sort
- T1.NO_CH, T2.NO_CH는 정렬되지 않음
3.4 Sort Merge Join
- 조인 연결고리의 역할 비교(1)
두 개 이상의 부등호 조인 조건이 있는 경우 조건 절에 기술된 순서대로
처음 조건이 조인주관조건이 되고 그 이후의 조건들은 조인체크조건이 됨
SELECT /*+ ORDERED USE_MERGE(T1 T2) */
T1.NO, T1.NO_CH, T2.NO, T2.NO_CH
FROM COPY_T1 T1, COPY_T2 T2
WHERE T1.NO_CH > T2.NO_CH
AND T1.NO < T2.NO
MERGE JOIN
SORT JOIN
TABLE ACCESS FULL COPY_T1
FILTER
SORT JOIN
TABLE ACCESS FULL COPY_T2
- 조인주관조건 : T1.NO_CH > T2.NO_CH
- 조인체크조건 : T1.NO < T2.NO
- 정렬은 T1.NO_CH, T2.NO_CH로 각각 Descending Sort
- T1.NO, T2.NO는 정렬되지 않음
3.4 Sort Merge Join
- 조인 연결고리의 역할 비교(2)
FLD1
= ‘AB’
TABLE
ACCESS
BY
ROWID
FULL
TABLE
SCAN
INDEX
(FLD1)
TAB1 TAB2
SELECT /*+ USE_MERGE(A B) */
A.FLD1, … B.FLD1
FROM TAB1 A, TAB2 B
WHERE A.KEY1 = B.KEY2
AND A.FLD1 = ‘AB’
AND B.FLD2 = ’10’
S
O
R
T
S
O
R
T
운반단위
a.KEY1 =
b.KEY2를
조건으로
MERGE
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
b.FLD2 = ’10’이 없으면
일의 양이 크게 증가
- 조인 컬럼 정렬에 큰 비용
- 정렬 후에는 빠른 결과 보장
- RBO : 인덱스가 없는 경우
- CBO : ALL_ROWS에 최적
(동등 조건에서는 HASH JOIN)
3.4 Sort Merge Join
- 특징
- 동시적으로 처리된다. 양쪽 집합이 모두 준비가 되어야 머지를 시작할 수 있으므로 순차적인
처리가 불가능하다.
- 각 집합이 준비작업을 할 때 다른 집합에서 처리한 결과를 제공받지 않는다. 즉 자신에게 주
어진 상수값에 의해서만 범위를 줄인다.
- 정렬 준비가 완료된 후에라야 조인을 시작할 수 있으므로 원초적으로 부분범위처리를 할 수
없어 항상 전체범위처리를 한다.
- 주로 스캔방식으로 처리된다. 각자의 처리범위를 줄이기 위해서 인덱스를 사용하는 경우만
랜덤 액세스가 발생함.
- 주어진 조건에 있는 모든 컬럼들이 인덱스를 가지고 있더라도 모두 사용되는 것은 아니다.
- 조인의 방향과는 거의 무관하다.
3.4 Sort Merge Join
- 특징
TAB_S
TAB_B
HASH
FUNCTION 1
Build
Input
결정
PARTITION
TABLE
HASH
TABLE
BITMAP
VECTOR
파티션수
결정
P1
C11
C12
P2 P3
C21
C22
C31
C32
C33
HASH AREA
①
②
③ HASH
FUNCTION 2
저장할
Partition
결정
Hash
value 생성
④
⑤
⑥
⑦
⑧ ⑨ ⑨
⑩
⑩
⑪
운반단위
SELECT /*+ USE_HASH(A B) FULL(A) FULL(B) */
FROM A.FLD1, …, B.COL1
FROM TAB_S A, TAB_B B
WHERE A.KEY1 = B.KEY2
AND A.FLD1 = ‘AB’
AND B.FLD2 = ’10’
3.5 Hash Join
- 개요
특 징
- 조인 건수가 적을 경우 NL 유리 , 데이터가 대용량 일 경우 Hash 조인 유리
- 순차적(부분범위처리가능)
- 연결고리 상태에 영향이 없음
제약사항
HASH_JOIN_ENABLED = FALSE 일 경우 옵티마이저는 hash join을 사용하는
실행계획을 선택 하지 않으려고 하며, 만약 hash 조인을 유도 하고자 할 경우
/*+ use_hash*/ 힌트를 활용
3.5 Hash Join
- 특징 및 제약사항
- Driving 쪽의처리 범위가 넓어 처리량이 매우 많은경우
- Random Access가 많은경우
- 연결고리 이상이 생겨 비이상적인 실행계획 or 반복 Range Scan이 일어날 경우
- 대상건수가 많고 두 집합간의 크기 차이가 심할 경우
- 대상건수가 많고 두 집합간의 크기 차이가 작을 경우에는 Sort Merge 조인이 유
리
단, 절대량이 많아서 Sort 부하가 아주 크다면 Hash 조인이 더 유리
3.5 Hash Join
- Hash Join은 언제 이용하는가?
SELECT /*+ ORDERED USE_NL(X Y) */
COUNT(Y.ACC_NM),
COUNT(Y.USR_CMPNY),
COUNT(X.ACC_CD), . . . . . .
FROM AC_MST X, AC_DTL Y
WHERE Y.USR_CMPNY = 'TOONI'
AND Y.CHNL_GB = 'CH_A'
AND Y.ACC_UNT = 'A'
AND X.ACC_CD = Y.ACC_CD
AND X.YEAR_NO = '07'
1 SORT (AGGREGATE)
63806 NESTED LOOPS
1057 INDEX (RANGE SCAN) OF ‘PK_AC_MST’ (UNIQUE)
63806 TABLE ACCESS (BY INDEX ROWID) OF ‘AC_DTL’
67380192 INDEX (RANGE SCAN) OF ‘PK_AC_DTL’ (UNIQUE
CPU : 195.09
Elapsed : 215.94
- PK_AC_MST : YEAR_NO + ACC_CD
- PK_AC_DTL : USR_CMPNY + CHNL_GB
+ ACC_UNT
SELECT /*+ ORDERED USE_HASH(X Y) */
COUNT(Y.ACC_NM),
COUNT(Y.USR_CMPNY),
COUNT(X.ACC_CD), . . . . . .
FROM AC_MST X, AC_DTL Y
WHERE Y.USR_CMPNY = 'TOONI'
AND Y.CHNL_GB = 'CH_A'
AND Y.ACC_UNT = 'A'
AND X.ACC_CD = Y.ACC_CD
AND X.YEAR_NO = '07'
1 SORT (AGGREGATE)
63806 HASH JOIN
1056 INDEX (RANGE SCAN) OF ‘PK_AC_MST’ (UNIQUE)
63806 TABLE ACCESS (BY INDEX ROWID) OF ‘AC_DTL’
63807 INDEX (RANGE SCAN) OF ‘PK_AC_DTL’ (UNIQUE)
CPU : 0.37
Elapsed : 0.37
3.5 Hash Join
- 예제
SELECT X.col1, X.col2, ……
FROM TAB1 X, TAB2 Y
WHERE X.key = Y.key
AND other_condition ……
조인
SELECT col1, col2, ……
FROM TAB1
WHERE key IN (SELECT key2
FROM TAB2
WHERE condition ……)
세미조인
TAB1 TAB2 조인 세미조인
1 1 1 1
1 M m 1
M 1 m m
M M m*m m
관계형태에 따라 전혀
다른 집합 생성
3.6 Semi Join
SELECT COL1, COL2
FROM TAB1 X
WHERE KEY1 IN (SELECT KEY2
FROM TAB2 Y
WHERE COL1 …
AND COL2 ……)
NESTED LOOPS
VIEW
SORT (UNIQUE)
TABLE ACCESS BY ROWID OF TAB2
INDEX RANGE SCAN OF COL1_IDX
TABLE ACCESS BY ROWID OF TAB1
INDEX RANGE SCAN OF KEY1_IDX
조인과 유사한
실행계획 생성
메인쿼리 집합을
보호하기 위해
UNIQUE하게 만듬
제공자 역할
3.6 Semi Join
- Nested Loop형 세미조인
SELECT 사번, 성명, 직급, 입사일, ……
FROM 사원 X
Where 부서 = ‘개발팀’
AND 사번 IN (SELECT 사번
FROM 가족 Y
WHERE Y.사번 = X.사번
AND Y.생년월일 < ‘19800101’)
FILTER
TABLE ACCESS BY ROWID OF ‘사원’
INDEX RANGE SCAN OF ‘부서_INDEX’
TABLE ACCESS BY ROWID OF ‘가족’
INDEX RANGE SCAN OF ‘PK_INDEX’
EXISTS와 유사
한 실행계획
조건을 만족하는
첫번째 로우를 만
나면 종료
확인자 역할
메인쿼리의 컬럼이 서브
쿼리에 있으면 서브쿼리
가 먼저 실행될수 없다.
3.6 Semi Join
- Nested Loop형 세미조인
UPDATE 청구 X
SET 입금액 = NVL(입금액, 0) + :IN_AMT
WHERE 청구년원 = ‘201101’
AND 고객번호 IN (SELECT 고객번호
FROM 고객 Y
WHERE 납입자 = :IN_CUST
AND Y.고객번호 = X.고객번호)
고객테이블 : 500만건 (INDEX : 납입자)
청구테이블 : 6000만건 (INDEX : 고객번호 + 청구년월)
고객테이블이 제공자 역할을
할 수가 없으므로 청구테이블
에 대해 매건 500만건을 읽어
야 함.
10000초
UPDATE 청구 X
SET 입금액 = NVL(입금액, 0) + :IN_AMT
WHERE 청구년원 = ‘201101’
AND 고객번호 IN (SELECT 고객번호
FROM 고객 Y
WHERE 납입자 = :IN_CUST)
0.1초
서브쿼리가 먼저 수행이 되어
서 고객테이블은 한번만 읽으
면 됨
3.6 Semi Join
- Nested Loop형 세미조인
SELECT …………………………
FROM ORDER X
WHERE ORDERDATE LIKE ‘201010%’
AND EXISTS (SELECT ‘X’
FROM DEPT Y
WHERE Y.DEPTNO = X.SALDEPT
AND Y.TYPE1 = ‘1’)
FILTER
TABLE ACCESS BY ROWID OF ‘ORDER’
INDEX RANGE SCAN OF ‘ORDDATE_INDEX’
TABLE ACCESS BY ROWID OF ‘DEPT’
INDEX RANGE SCAN OF ‘DEPT_PK’
FILTER
TABLE ACCESS BY ROWID OF ‘ORDER’
INDEX RANGE SCAN OF ‘ORDDATE_INDEX’
TABLE ACCESS BY ROWID OF ‘DEPT’
INDEX RANGE SCAN OF ‘DEPT_PK’
3200
3200
3201
10
10
실제로는 DEPT를 10번만 엑
세스 하였다.
어떻게 된 일일까?
3.6 Semi Join
- 필터(Filter)형 세미조인
20100930 … 22
20101001 … 11
20101002 … 11
20101001 … 11
20101002 … 11
20101003 … 11
20101001 … 22
20101002 … 22
20101006 … 22
11 1
22 2
11
access
11 1
BUFFER
22
저장
저장
access
CHECK
CHECK
Buffer내용
과 다르면
Access 수행
ORDDATE_INDEX ORDER DEPT_PK DEPT
같으면 Buffer
만 Check하고
완료
새로 액세스한 것은
buffer에 저장
ORDERDATE + SALDEPT로 인덱스가 있다면 DEPT를 액세스 하러 가는 경우가 최소화
22 2
3.6 Semi Join
- 필터(Filter)형 세미조인
SELECT …………………………
FROM ORDER X
WHERE ORDERDATE LIKE ‘201010%’
AND EXISTS (SELECT /*+ HASH_SJ(X, Y) */ ‘X’
FROM DEPT Y
WHERE Y.DEPTNO = X.SALDEPT
AND Y.TYPE1 = ‘1’)
HASH JOIN SEMI
TABLE ACCESS BY ROWID OF ‘ORDER’
INDEX RANGE SCAN OF ‘ORDDATE_INDEX’
TABLE ACCESS FULL OF ‘DEPT’
- 연결고리 연산자는 반드시 ‘=‘이 되어야 함
- 서브쿼리 내에 GROUP BY, CONNECT BY, ROWNUM을 사용할 수 없다는 제약이 있음
3.6 Semi Join
- 해쉬(Hash)형 세미조인
SELECT *
FROM CUSTOMERS C
WHERE EXISTS
(SELECT 'X'
FROM SALES_T1 SA
WHERE SA.CUST_ID = C.CUST_ID
AND AMOUNT_SOLD > 10000 )
41 FILTER
50000 TABLE ACCESS FULL CUSTOMERS
41 TABLE ACCESS BY INDEX ROWID SALES_T1
2740210 INDEX RANGE SCAN IX_CUST_ID
25초
FILTER로 수행되는 것이
효율적인가?
41 HASH JOIN SEMI
50000 TABLE ACCESS FULL CUSTOMERS
280 TABLE ACCESS FULL SALES_T1
5초
41 FILTER
50000 TABLE ACCESS FULL CUSTOMERS
41 TABLE ACCESS BY INDEX ROWID SALES_T1
2740210 INDEX RANGE SCAN IX_CUST_ID
27초
/*+ HASH_SJ */ /*+ NL_SJ */
본래는 분산환경에서 분산질의 시 데이터 전송량 감소를 통해 질의를 최적화하기 위한
방법으로 제시
3.6 Semi Join
- Filter 수행 시 발생되는 랜덤 Access의 부하를 감소시킨다.
- 대용량 데이터 처리시 HASH_SJ, HASH_ANTI를 활용 하는 것이 유리함
(MAIN 집합이 크면 서브쿼리의 수행부하가 커지기 때문에-전제범위 처리 시)
ex) 배치 작업, 다량의 데이터 ETL, 데이터 Migration시 데이터 검증시 활용
- NL_SJ, NL_AJ는 Filter 보다 불리
SEMI JOIN의 제약사항
- Subquery 내에서 하나의 Table만 참조 해야함
- Subquery안의 Subquery에는 사용 못함
- Subquery에서의 Main Table과의 연결은 Equal만 가능
- Subquery안에 GROUP BY, CONNECT BY, ROWNUM을 사용하지 못함
- Main SQL에서 조인이 있는 경우는 사용 불가능
- 비용기준 옵티마이져 모드(CBO) 일 때만 사용가능
3.6 Semi Join
- Semi Join 활용방안 및 제약사항
SELECT /*+ ORDERED USE_HASH( X Y)
FULL(Y) NOPARALLEL(Y) */
COUNT(*)
FROM STG_CUSTOMER Y
WHERE CUSTOMER_ID
IN (SELECT /*+ NOPARALLEL(X)
FULL(X) */
CUSTOMER_ID
FROM IN_CUSTOMER X)
1 SORT AGGREGATE
128448 HASH JOIN
16598176 SORT UNIQUE
16598176 TABLE ACCESS FULL IN_CUSTOMER
128641 TABLE ACCESS FULL STG_CUSTOMER
196.5
sec
SELECT /*+ NOPARALLEL(Y) FULL(Y) */
COUNT(*)
FROM STG_CUSTOMER Y
WHERE CUSTOMER_ID
IN (SELECT /*+ NOPARALLEL(X)
HASH_SJ(X) */
CUSTOMER_ID
FROM IN_CUSTOMER X)
1 SORT AGGREGATE
128448 HASH JOIN SEMI
16598176 SORT UNIQUE
16598176 TABLE ACCESS FULL IN_CUSTOMER
128641 TABLE ACCESS FULL STG_CUSTOMER
45.3 sec
* 제공자 역할로 수행한 경우 * 확인자 역할로 수행한 경우(SEMI 조인 방식)
3.6 Semi Join
- 사례
SELECT COUNT(*)
FROM TAB1
WHERE COL1 like ‘ABC%’
AND COL2 NOT IN
(SELECT FLD2
FROM TAB2
WHERE FLD3 LIKE ‘1998%’ )
3.7 Anti Join
FILTER
TABLE ACCESS BY ROWID OF ‘TAB1’
INDEX RANGE SCAN OF ‘COL1_INDEX’
TABLE ACCESS BY ROWID OF ‘TAB2’
INDEX RANGE SCAN OF ‘FLD3_INDEX’
COL1이 처리범위가 넓다면 서브쿼
리가 다량의 랜덤 액세스가 발생함.
SORT_MERGE
ANTI
조인으로 유도
HASH ANTI
조인으로 유도
3.7 Anti Join
- MERGE ANTI 조인
SELECT COUNT(*)
FROM TAB1
WHERE COL1 like ‘ABC%’
AND COL2 IS NOT NULL
AND COL2 NOT IN
(SELECT /*+ MERGE_AJ */
FLD2
FROM TAB2
WHERE FLD3 LIKE ‘1998%’
AND FLD2 IS NOT NULL
)
MERGE JOIN (ANTI)
SORT (JOIN)
TABLE ACCESS (BY ROWID) OF 'TAB1'
INDEX (RANGE SCAN) OF 'COL1_IDX'
SORT (UNIQUE)
VIEW
TABLE ACCESS (BY ROWID) OF 'TAB2'
INDEX (RANGE SCAN) OF 'FLD3_IDX'
MERGE대상 칼럼이 NOT NULL일 경우
IS NOT NULL 조건 불필요
NOT NULL 지정
NOT NULL 지정
3.7 Anti Join
- HASH ANTI 조인
SELECT COUNT(*)
FROM TAB1
WHERE COL1 like ‘ABC%’
AND COL2 IS NOT NULL
AND COL2 NOT IN
(SELECT /*+ HASH_AJ */
FLD2
FROM TAB2
WHERE FLD3 LIKE ‘1998%’
AND FLD2 IS NOT NULL
)
HASH JOIN (ANTI)
SORT (JOIN)
TABLE ACCESS (BY ROWID) OF 'TAB1'
INDEX (RANGE SCAN) OF 'COL1_IDX'
VIEW
TABLE ACCESS (BY ROWID) OF 'TAB2'
INDEX (RANGE SCAN) OF 'FLD3_IDX'
MERGE대상 칼럼이 NOT NULL일 경우
IS NOT NULL 조건 불필요
NOT NULL 지정
NOT NULL 지정
NESTED LOOPS 조인
- Driving 컬럼 조건
- 인덱스 및 연결 고리 정상 여부
- 조인 순서
SORT MERGE 조인
- 가용 메모리 혹은 SORT_AREA_SIZE값
- DB_FILE_MULTIBLOCK_READ_COUNT
- Degree of Parallelism
HASH 조인
- Build 테이블의 조인대상 집합의 크기
- 가용 메모리 혹은 HASH_AREA_SIZE
- DB_FILE_MULTIBLOCK_READ_COUNT
- Degree of Parallelism
A B
JOIN
3.8 조인방법 결정 및 성능 개선 팩터
Driving
Table
결정
부
분
범
위
처
리
Driving
조건
Nested
Loop
Join
Sort
Merge
Join
Check
조건
Driving과
Check조
건 교환
선행처리 집합으로 부터 값을
받은 경우와 받지 않은 경우를
비교
가능 좁다
넓다
넓다
좁다
불가능
유리
불리
가능
불가능
3.9 조인 방법의 결정
[실습3.1] 아래 쿼리의 실행계획을 확인하고 순서대로 설명하라.
SELECT ROWNUM rnum, N.qna_id FROM (
SELECT /*+ USE_NL(Q P) */ Q.qna_id
FROM t_qna Q, (
SELECT qna_id, MAX(created_date) created_date
FROM t_qna_process QP
WHERE
((account_id = 'admin1' AND type IN ('ANEND', 'ANADD') )
OR (previous_account_id = 'admin1' AND type = 'ANFWD'))
GROUP BY qna_id
) P
WHERE Q.qna_id = P.qna_id AND Q.delete_flag = 'N' AND Q.spam_flag='N'
AND Q.created_date >= '00000000000000'
ORDER BY Q.updated_date DESC, Q.qna_id DESC
) N
WHERE ROWNUM <= 10
[실습3.2] 아래 쿼리의 실행계획을 확인하고 순서대로 설명하라.
SELECT env_id, target_id, target_type, env_value, created_by, created_date, updated_by, updated_date
FROM t_env_value E,
(
SELECT N.node_id FROM t_node_account_rel R, t_node N
WHERE R.node_id = N.node_id
AND R.account_id='admin1' AND R.module_type='SVQNA' AND N.node_type='DNODE'
) D
WHERE E.target_id = D.node_id
AND E.env_id IN
(
'QNA_AGENT_DELETE_USE_FLAG' ,
'QNA_AGENT_REROUTE_USE_FLAG' ,
'QNA_APPROVAL_USE_FALG' ,
'QNA_ANSWER_TITLE_PREFIX' ,
'QNA_AGENT_ATTACH_USE_FLAG'
)
ORDER BY E.target_id
[실습3.3] 아래 쿼리의 실행계획을 확인하고 순서대로 설명하라.
SELECT * FROM t_account ACC, (
SELECT A.* FROM (
SELECT A.account_id, COUNT(Q.qna_id) AS not_answer_count FROM t_account A
LEFT OUTER JOIN t_qna Q ON A.account_id = Q.account_id
AND Q.delete_flag = 'N' AND Q.spam_flag = 'N'
AND (Q.answer_status IN ('ANNOT', 'ANFWD', 'ANTMP')
OR (Q.answer_status IN ('ANEND') AND Q.approval_status IN ('APREJ', 'APCCL')))
WHERE A.delete_flag = 'N'
AND EXISTS (
SELECT 1 FROM t_node_account_rel R, t_node N
WHERE R.node_id = N.node_id AND R.account_id = A.account_id
AND R.module_type = 'SVQNA' AND N.delete_flag = 'N'
)
GROUP BY A.account_id
) A
) A, t_role R
WHERE ACC.account_id = A.account_id
AND ACC.role_id = R.role_id
[실습3.4] 아래 쿼리의 실행계획을 확인하고 순서대로 설명하라.
SELECT * FROM t_account A
WHERE A.delete_flag = 'N‘ AND A.role_id IN ( 'ACCTMGR','ACCTADM')
AND EXISTS (
SELECT 1 FROM t_node_account_rel
WHERE account_id = A.account_id AND module_type = 'SVQNA'
)
ORDER BY account_id
SELECT COUNT(*)
FROM t_qna Q
WHERE Q.delete_flag = 'N'
AND Q.spam_flag='N' AND Q.created_date >= '00000000000000'
AND Q.answer_status IN ('ANEND', 'ANADD')
AND EXISTS (
SELECT 1 FROM t_qna_process WHERE qna_id = Q.qna_id
AND type = 'ANEND'
AND created_date LIKE TO_CHAR(SYSDATE, 'YYYYMMDD') || '%'
)
[실습3.5] 아래 쿼리의 실행계획을 확인하고 순서대로 설명하라.
SELECT * FROM t_template T, t_node N
WHERE T.delete_flag = 'N'
AND T.domain_id = N.node_id
AND N.node_type ='DNODE'
AND EXISTS (
SELECT /*+ no_unnest */ T.service_type
FROM t_node_service S
WHERE S.node_id = T.domain_id
AND S.service_type = T.service_type
AND S.use_flag='Y')
AND service_type IN
(
'SVKNW' , 'SVCHT' , 'SVQNA')
AND ROWNUM <= 10
no_unnest를 없앤다면?
[실습3.6] 아래 쿼리에서 힌트를 사용하여 등록일자 오름차순으로 결과가 나오
도록 쿼리를 변경하라.
SELECT Q.*
FROM t_node N, t_qna Q, t_qna_process QP
WHERE N.node_id = Q.node_id
AND Q.qna_id = QP.qna_id
AND Q.delete_flag = 'N'
AND Q.answer_status IN ('ANNOT', 'ANFWD', 'ANTMP')
AND Q.approval_status ='APNOT'
AND Q.created_date LIKE '2010%'
AND N.delete_flag = 'N'
등록일자 내림차순으로 가져오려면?
[실습3.7] 아래 쿼리를 최근 등록일자 순으로 나오게 변경하라.
SELECT *
FROM (
SELECT ROWNUM RNUM, Q.*
FROM t_qna Q
WHERE delete_flag = 'N' AND spam_flag='N'
AND Q.created_date LIKE '2010%'
AND node_id IN
(
SELECT /*+ no_unnest */ node_id FROM t_node START WITH node_id='NODE0000000001'
CONNECT BY PRIOR node_id=parent_id
)
AND ROWNUM <= 10
ORDER BY Q.created_date DESC, Q.rowId
) Q LEFT OUTER JOIN t_qna_option OP
ON Q.qna_id = OP.qna_id
WHERE rnum >= 1
[실습3.8] 아래는 현재 EE에서 사용하는 특정 기간 내에 특정 카테고리 하위
의 qna개수를 가져오는 쿼리이다.
EE에서 아래쿼리를 사용하고 있는데 t_qna데이타가 많을 경우 느려질수 있다.
왜 그런가?
SELECT count(*)
FROM t_qna Q
WHERE Q.delete_flag = 'N'
AND Q.spam_flag='N'
AND Q.created_date >= '20101122000000‘
AND Q.node_id IN
(
SELECT /*+ no_unnest */ node_id
FROM t_node N
START WITH node_id IN ( 'NODE0000000000' )
CONNECT BY PRIOR node_id=parent_id
)
I. SQL 실행계획
II. 인덱스(index)
III. 조인 최적화
IV. 부분범위 처리
4.1 부분범위 처리의 기본개념
4.2 전체범위 vs 부분범위
4.3 부분범위 처리의 적용원칙
4.4 부분범위 처리의 수행속도 향상 원리
4.5 부분범위 처리의 적용원칙
4.1 부분범위처리의 기본개념
4.2 전체범위처리 vs 부분범위처리
전체 범위 처리 부분 범위 처리
Full Range Scan 후 가공하여
Array Size 만큼 추출
조건을 만족하는 Row 수가
Array Size에 도달되면 멈춤
운반단위 운반단위
SELECT SUM(ordqty)
FROM order
WHERE ord_date LIKE ‘201001%’
GROUP BY ord_dept
Select-list절에 sum,
count가 있으므로
조건의 일부분만 엑
세스는 불가능 Select-list나 where
절에 그룹함수 사용
하면 부분범위 처리
를 할 수 없다.
SELECT *
FROM order
WHERE ord_date LIKE ‘201001%’
ORDER BY ord_date
ORDER BY가 사용
되면 전체범위를 처
리할 수 밖에 없다.
ord_date가 선두에
위치한 인덱스가 존
재하면 부분범위 처
리를 할수 있다.
4.3 부분범위처리의 적용원칙
- 부분범위처리의 자격
SELECT deptno, emp
FROM emp1
WHERE sal > 10000000
UNION
SELECT deptno, empno
FROM emp2
WHERE hiredate BETWEEN ‘20100101’ AND ‘20101231’
UNION, MINUS, INTERSECT를
사용한 SQL은 부분범위로 처리할
수 없다.
그 결과가 반드시 유일(UNIQUE,
DISTINCT) 해야 하기 때문
UNION ALL을 이용
하면 부분범위 처리
가 가능하다.
4.3 부분범위처리의 적용원칙
- 부분범위처리의 자격
SELECT * FROM ORDER
0.01 sec
SELECT * FROM ORDER ORDER BY item
10 sec
SELECT * FROM ORDER WHERE item > ‘ ‘
0.01 sec
SELECT /*+ INDEX(A item_index) * FROM ORDER A
WHERE item > ‘ ‘
0.01 sec
4.4 부분범위처리의 수행속도 향상 원리
- 액세스 경로를 이용한 SORT의 대체
- 인덱스만 처리하는 부분범위 처리
- MIN, MAX의 처리
- FILTER형 부분범위 처리
- ROWNUM을 이용한 부분범위 처리
- 인라인뷰를 이용한 부분범위 처리
4.5 부분범위처리의 적용원칙
- 부분범위 처리로의 유도
SELECT ord_dept, ordqty * 1000
FROM order
WHERE ord_date LIKE ’2009%’
ORDER BY ord_dept DESC
액세스 주관 컬럼은 ord_date이다.
ord_dept로 역순정렬을 할려면…
SELECT /*+ INDEX_DESC(A ord_dept_index) */
ord_dept, ordqty * 1000
FROM order
WHERE ord_date LIKE ’2009%’
AND ord_dept >’ ‘
ord_dept를 역순으로 정렬함으로
써 부분범위 처리 가능
4.5 부분범위처리의 적용원칙
- 엑세스 경로를 이용한 SORT의 대체
SELECT ord_date, SUM(qty)
FROM order
WHERE ord_date LIKE ’2009%’
GROUP BY ord_date
ord_date인덱스에 qty를 합친 결
합인덱스 생성하면 인덱스만 사용
함.
G
R
O
U
P
B
Y
운반단위 운반단위
G
R
O
U
P
B
Y
index
(ord_date)
table (order) Index
(ord_date + qty)
4.5 부분범위처리의 적용원칙
- 인덱스만 액세스 하는 부분범위 처
리
SELECT /*+ INDEX_DESC(A pk_order) */
NVL(MAX(seq), 0) + 1
FROM order A
WHJERE dept_no = ‘123000’
AND ROWNUM = 1
SELECT MAX(seq)
FROM order
WHJERE dept_no = ‘123000’
SORT (AGGREGATE)
FIRST ROW
INDEX RANGE SCAN (MIN/MAX) OF ‘PK_ORDER’ (UNIQUE)
단 한건만 엑세스 하겠다는 의미
오라클 버젼별로 다름
데이터가 없을 경우는 NO DATA FOUND.
그룹함수는 언제나 성공(SUCCESS)
4.5 부분범위처리의 적용원칙
- MIN, MAX의 처리
SELECT MAX(seq)
FROM order
WHJERE dept_no = ‘123000’
SORT (AGGREGATE)
INDEX RANGE SCAN ‘PK_ORDER’ (UNIQUE)
10.2초
0.01초
RBO
CBO
SELECT NVL(ename, ‘홍길동’) FROM emp
WHERE 1=2
결과는?
NULL이다
NULL과 그룹함수와 관계에 대해서 알아보자
No rows returned.
SELECT COUNT(ename) FROM emp
WHERE 1=2
결과는?
0이다
SELECT MAX(ename) FROM emp
WHERE 1=2
결과는?
NULL이다
NVL함수는 한건이라도 데이터가 있는 경우에 적
용이 된다.
SELECT NVL(ename, ‘홍길동’) FROM emp
WHERE 1=2
결과가 나오게 할려면 어떻게 해야할까?
NULL과 그룹함수와 관계에 대해서 알아보자
SELECT NVL(MAX(ename), ‘홍길동’) FROM emp
WHERE 1=2
결과는 홍길동이 나온다.
그룹함수는 최소 한건의 데이터는 나온다.
Sort
(aggregate)
운반단위
운반단위
index
(dept)
table
(item_tab)
Index
(dept)
table
(item_tab)
SELECT COUNT(*) INTO :CNT
FROM ITEM_TAB
WHERE DEPT = ‘101’
AND SEQ > 100
……
If CNT > 0
SELECT INTO :CNT FROM DUAL
WHERE EXISTS (SELECT ‘X’
FROM ITEM_TAB
WHERE DEPT = ‘101’
AND SEQ > 100
……
If CNT > 0
4.5 부분범위처리의 적용원칙
- Filter형 부분범위 처리
4.5 부분범위처리의 적용원칙
- ROWNUM의 활용
운반단위
index
(dept)
table
(item_tab)
①
②
③
⑩
COUNT (STOPKEY)
TABLE ACCESS BY ROWID OF ‘DEPT’
INDEX RANGE SCAN OF ‘PK_DEPT’
4.5 부분범위처리의 적용원칙
- ROWNUM의 활용
운반단위
index
(dept)
table
(item_tab)
1
2
3
4
5
6
7
8
9
10
3
5
1
8
2
6
9
10
4
7
SELECT ROWNUM, item_cd, category_cd, …
FROM product
WHERE deptno LIKE ‘120%’
And QTY > 0
AND ROWNUM <= 10
ORDER BY item_id
4.5 부분범위처리의 적용원칙
제대로된 결과를 얻을려면 아래의 형식으로 변경해야 한다.
SELECT ROWNUM, item_cd, category_cd, …
FROM (
SELECT * FROM product
WHERE deptno LIKE ‘120%’
AND QTY > 0
ORDER BY item_id
) A
WHERE ROWNUM <= 10
4.5 부분범위처리의 적용원칙
- 인라인뷰를 이용한 부분범위처리
SELECT A.dept_name, b.emp_no, B.emp_name, C.sal_ym, C.sal_tot
FROM department A, employee B, salary C
WHERE B.deptno = A.deptno
AND C.empno = B.empno
AND A.location = ‘SEOUL’
AND B.job = ‘MANAGER’
AND C.sal_ym = ‘201012’
ORDER BY A.dept_name, B.hire_date, C.sal_ym
서울에 근무하는 매니저들의 입사일 순으로 급
여액 조회
4.5 부분범위처리의 적용원칙
- 인라인뷰를 이용한 부분범위처리 (수정)
SELECT /*+ ORDERED USE_NL(X Y) */
A.dept_name, b.emp_no, B.emp_name, C.sal_ym, C.sal_tot
FROM (
SELECT A.dept_name, b.emp_no, B.emp_name
FROM department A, employee B
WHERE B.deptno = A.deptno
AND A.location = ‘SEOUL’
AND B.job = ‘MANAGER’
ORDER BY A.dept_name, B.hire_date, C.sal_ym) X, salery Y
WHERE Y.empno = X.empno
AND Y.sal_ym = ‘201012’
empno + sal_ym 인덱스를 경유
Department, employee는 전체범위
처리를 하지만 salary는 부분범위 처리
가 가능하다.
[실습4.1] 답변완료가 된 건들을 최근날짜 순으로 가져오는데 부분범위 처리
가 가능하게 변경하라.
SELECT *
FROM t_qna A, t_qna_process B
WHERE A.qna_id = B.qna_id
AND A.delete_flag = 'N'
AND A.answer_status = 'ANEND'
ORDER BY A.created_date DESC
[실습4.2] 처리일자 별 건수를 가져오는 쿼리이다. 부분범위처리가 가능하게
인덱스를 추가해라.
SELECT SUBSTR(answer_date, 0, 6), COUNT(*)
FROM t_qna
WHERE answer_date LIKE '2010%'
AND answer_status = 'ANEND'
GROUP BY SUBSTR(answer_date, 0, 6)
[실습4.3] qna_id의 키를 가져오는 쿼리이다.
부분범위처리를 이용해서 쿼리를 변경하라.
SELECT created_key
FROM
(
SELECT 'QNAS'||LPAD(NVL(MAX(SUBSTR(qna_id,5))+1, '0000000001'), 10, '0') as created_key
FROM t_qna
WHERE qna_id > '0000000000'
ORDER BY qna_id DESC
)
WHERE ROWNUM = 1
[실습4.4] 사용자 현황별 등록건수를 가져오는 쿼리의 일부분이다.
아래 쿼리는 데이터가 많을 경우 문제가 될 수 있는데 그 원인과 쿼리를 수정하
라.
SELECT fn_get_domain_in_account(A.account_id, 'SVQNA', '/') AS domain_names,
not_answer_count, today_answer_count, role_id, account_name, login_flag, work_status
FROM t_account A,
(
SELECT account_id, SUM(not_answer_count) not_answer_count,
SUM(today_answer_count) today_answer_count
FROM (
SELECT account_id, COUNT(*) not_answer_count, 0 today_answer_count
FROM t_qna
WHERE delete_flag='N' AND spam_flag='N' AND answer_status IN ('ANNOT', 'ANFWD', 'ANTMP')
GROUP BY account_id
UNION ALL
SELECT account_id, 0 not_answer_count, COUNT(*) today_answer_count
FROM t_qna
WHERE delete_flag='N' AND spam_flag='N' AND answer_status IN ('ANEND', 'ANADD')
AND answer_date >= '20101105' || '000000' AND answer_date <= '20101105' || '999999'
GROUP BY account_id ) NT
GROUP BY account_id ) S
WHERE A.account_id = S.account_id
[실습4.5] 미처리된 qna의 최근순으로 10건만 가져오는 쿼리이다. 아래 쿼리
를 부분범위 처리를 이용하여 가져와라.
SELECT * FROM (
SELECT ROWNUM rnum, Q.*
FROM t_qna Q, t_qna_process B
WHERE Q.qna_id = B.qna_id
AND spam_flag = 'N'
AND delete_flag = 'N'
AND (answer_status IN ('ANNOT','ANTMP','ANFWD')
OR (answer_status IN ('ANEND', 'ANADD') AND approval_status IN('APREJ','APCCL'))
)
AND Q.created_date <= '99991231000000'
AND ROWNUM <= 10
ORDER BY Q.created_date DESC
) Q
WHERE rnum >= 1
V. MSSQL 쿼리
VI. 제품 쿼리 살펴보기
VII. 쿼리 작성 팁
VIII. 에필로그
1.1 인덱스
1.2 조인
5.1 인덱스
클러스터 인덱스
- 인덱스의 리프레벨이 데이타 페이지인 인덱스로서 데이타 페이지의 행들은 인덱스
키 값의 순으로 정렬되어 있다.
- UNIQUE 키워드와 함께 만들어지고 키 값들이 고유한 값을 가지도록 되어있다.
- 일반적으로 별도로 지정하지 않으면 Primary Key가 Cluster Index로 생성이 된다.
- 테이블당 1개의 Cluster Index만 만들어진다.
인덱스 페이지 데이타 페이지
비잎 수준
잎 수준
5.1 인덱스
넌클러스터 인덱스
- 데이타 페이지는 정렬되어 있지 않고 리프레벨 인덱스 페이지에서 각 행의 ID를 가
진다.
- 테이블당 여러개의 NonCluster Index가 생성이 된다.
인덱스 페이지
데이타 페이지
비잎 수준
잎 수준
잎 수준
(키 값)
5.1 인덱스
- 인덱스 정보보기
SP_HELPINDEX 테이블명
- 인덱스 생성
CREATE INDEX 인덱스명 ON 테이블명(컬럼명, ...) WITH DROP_EXISTING
5.1 인덱스
복합인덱스는 둘 이상의 컬럼으로 만드는 인덱스이다.
복합인덱스
쿼리 검색에 필요한 모든 컬럼들이 포함된(넌클러스터) 인덱스를 말한다.
인덱스 검색 후 북마크 룩업을 해야 하는데 필요한 모든 컬럼 값이 인덱스에 이미 다
있으므로 북마크 룩업 과정은 생략된다.
커버된 인덱스
둘 이상의 인덱스를 동시에 사용하여 인덱스 교집합을 만든다.
인덱스 교집합
5.2 조인
중첩루프(Nested Loop) 조인
병합(Merge) 조인
해쉬(Merge) 조인
V. MSSQL 쿼리
VI. 제품 쿼리 살펴보기
VII. 쿼리 작성 팁
VIII. 에필로그
1.1 eNomix 5.4.2
1.2 EE
6.1 eNomix 5.4.2
SELECT * FROM (
SELECT /*+ INDEX_DESC(N2 idx_noticeboard_board) */ * FROM (
SELECT /*+ ORDERED */ ROWNUM rseq, N.*
FROM t_noticeboard N, t_ticketbox T
WHERE T.tb_id = N.tbox_id AND N.ref > 0
AND N.register_date >= '20000101000000' AND N.register_date <= '20201231000000'
AND RTRIM(N.tbox_id) IN (
SELECT tb_id FROM t_ticketbox WHERE tb_parent_id = 'TBOXCATEGORIESPOOL'
)
AND N.flag_delete = 'N'
AND ROWNUM <= 10
) N2
) N3
WHERE rseq >= 0
1. 공개 게시판
인덱스 ref DESC + step
오라클 버젼별로 순서가 맞지 않게 나오는 문제가 있음
Ref순으로 나오게 할려면 어떻게 변경을 해야 할까?
url : devora.spectra.co.kr
sid : devora
Id/pwd : mail_lotte/mail_lotte
6.1 eNomix 5.4.2
SELECT * FROM (
SELECT /*+ USE_NL(N T) INDEX(N idx_noticeboard_board) */ ROWNUM rseq, N.*
FROM t_noticeboard N,
(
SELECT tb_id FROM t_ticketbox WHERE tb_parent_id = 'TBOXCATEGORIESPOOL'
) T
WHERE T.tb_id = N.tbox_id AND N.ref > 0
AND N.register_date >= '20000101000000' AND N.register_date <= '20201231000000'
AND N.flag_delete = 'N'
AND ROWNUM <= 10
) A
WHERE rseq >= 0
1. 공개 게시판(수정)
인덱스 ref DESC + step
SELECT * FROM (
SELECT /*+ INDEX_DESC(N2 idx_noticeboard_board) */ * FROM (
SELECT /*+ ORDERED */ ROWNUM rseq, N.*
FROM t_noticeboard N, t_ticketbox T“
WHERE T.tb_id = N.tbox_id AND N.ref > 0
AND N.member_id = ‘test’
AND N.register_date >= '20000101000000' AND N.register_date <= '20201231000000'
AND RTRIM(N.tbox_id) IN (
SELECT tb_id FROM t_ticketbox WHERE tb_parent_id = ‘'TBOXCATEGORIESPOOL’
)
AND N.flag_delete = 'N‘
AND ROWNUM <= 10
) N2
) N3
WHERE rseq >= 0
2. 나의 문의 내용보기
member_id에 대한 인덱스가 없음.
Ref순으로 나오지 않음
Member_id에 대해서 ref순으로 나오게 할려면?
인덱스 ref DESC + step
6.1 eNomix 5.4.2
SELECT * FROM (
SELECT /*+ USE_NL(N T) INDEX(N idx_noticeboard_member_board) */ ROWNUM
rseq, N.*
FROM t_noticeboard N,
(
SELECT tb_id FROM t_ticketbox WHERE tb_parent_id = 'TBOXCATEGORIESPOOL'
) T
WHERE T.tb_id = N.tbox_id AND N.ref > 0
AND N.register_date >= '20000101000000' AND N.register_date <=
'20201231000000'
AND N.flag_delete = 'N'
AND ROWNUM <= 10
) A
WHERE rseq >= 0
2. 나의 문의 내용보기 (수정)
member_id에 대한 인덱스가 없음.
날짜순으로 나오지 않음
member_id, ref DESC, step 로 인덱스를 생성
6.1 eNomix 5.4.2
SELECT *
FROM t_ticketbox
START WITH tb_id = 'TBOXCATEGORIESPOOL'
CONNECT BY PRIOR tb_id = tb_parent_id
3. 카테고리 정보 가져오기
6.1 eNomix 5.4.2
위의 쿼리를 실행시키면 실행이 안된다.
어떻게 수정을 해야할까?
SELECT *
FROM (
SELECT * FROM t_ticketbox WHERE tb_id < 'TBOXCATEGORIESPOOL'
) A
START WITH tb_parent_id = 'TBOXCATEGORIESPOOL'
CONNECT BY PRIOR tb_id = tb_parent_id
3. 카테고리 정보 가져오기 (수정)
6.1 eNomix 5.4.2
TBOXCATEGORIESPOOL은 tb_id와 tb_parent_id가 같
아서 무한루프를 돈다.
SELECT * FROM (
SELECT rownum ROW_NUM, BL.* FROM (
SELECT T4.question_id, T4.question_body, T2.category_id, T3.category_name,T3.parent_id,
T1.frequency counts
FROM t_question T1, t_answer T2 , t_category T3, t_user_top_n_01 T4
WHERE T4.question_id = T1.question_id AND T1.answer_id = T2.answer_id
AND T2.category_id = T3.category_id
AND T3.web_view ='VIEW00' AND T3.flag_delete='NOTDEL' AND T2.web_view = 'VIEW00'
AND T2.flag_delete='NOTDEL'
AND T1.question_type <> 'TY_RAR' AND T1.flag_delete='NOTDEL'
ORDER BY T4.sort_order
) BL
) WHERE ROW_NUM <= 10
4. TOP-N 가져오기
6.1 eNomix 5.4.2
FAQ건수가 5만건 이라면 어떤 인덱스를
추가해야 하고 쿼리를 어떻게 변경을 해야
할까?
작성필요
4. TOP-N 가져오기
6.1 eNomix 5.4.2
FAQ건수가 5만건 이라면 어떤 인덱스를
추가해야 하고 쿼리를 어떻게 변경을 해야
할까?
SELECT A.* FROM (
SELECT ROWNUM rnum, A.*
FROM (
SELECT A.*
FROM v_qna_board A,
( SELECT * FROM t_node START WITH node_id = 'NODE0000000001' CONNECT BY PRIOR nod
e_id = parent_id) C
WHERE A.board_node_id = C.node_id
AND A.webview_flag = 'Y' AND A.delete_flag = 'N'
AND A.customer_id = '1'
ORDER BY A.created_date DESC
) A
WHERE ROWNUM <= 10
) A
WHERE rnum >= 1
6. 나의 문의 리스트
6.2 EE
회원별 전체 데이터를 정렬을 한 다음 10
건을 가져온다.
인덱스를 통한 10건만 엑세스 할려면 어
떻게 변경을 해야할까?
SELECT A.*
FROM (
SELECT ROWNUM rnum, A.*
FROM (
SELECT /*+ USE_NL(A C) INDEX_DESC(A IDX_QNA_CUSTOMER_ID_DATE) */ A.*
FROM t_qna A, t_qna_board B,
(
SELECT * FROM t_node START WITH node_id = 'NODE0000000001' CONNECT BY PRIOR
node_id = parent_id) C
WHERE A.qna_id = B.qna_id
AND A.node_id = C.node_id
AND B.webview_flag = 'Y'
AND A.delete_flag = 'N'
AND A.customer_id = '1'
AND A.created_date > '00000000000000'
) A
WHERE ROWNUM <= 10
) A
WHERE rnum >= 1
6.2 EE 인덱스 추가해야 함.
customer_id, created_date
6. 나의 문의 리스트
SELECT A.* FROM (
SELECT ROWNUM rnum, A.*
FROM (
SELECT A.*
FROM v_qna_board A,
(
SELECT * FROM t_node START WITH node_id = 'NODE0000000001' CONNECT BY PRIOR
node_id = parent_id) C
WHERE A.board_node_id = C.node_id
AND A.webview_flag = 'Y' AND A.delete_flag = 'N'
ORDER BY A.created_date DESC
) A
WHERE ROWNUM <= 10
) A
WHERE rnum >= 1
7. 전체 게시판이라면
6.2 EE
SELECT A.*
FROM (
SELECT ROWNUM rnum, A.*
FROM (
SELECT /*+ USE_NL(A B C) INDEX_DESC(A IDX_QNA_CRE_DATE_ASC_SPAM_DEL) */ A.*
FROM t_qna A, t_qna_board B,
(
SELECT * FROM t_node START WITH node_id = 'NODE0000000001' CONNECT BY PRIOR
node_id = parent_id) C
WHERE A.qna_id = B.qna_id
AND A.node_id = C.node_id
AND B.webview_flag = 'Y'
AND A.delete_flag = 'N'
AND A.created_date > '00000000000000'
) A
WHERE ROWNUM <= 10
) A
WHERE rnum >= 1
7. 전체 게시판이라면
6.2 EE
SELECT qna_id
FROM (
SELECT A.qna_id
FROM v_qna_board A, (
SELECT * FROM t_node START WITH node_id = 'NODE0000000001' CONNECT BY PRIOR
node_id = parent_id
) B
WHERE A.qna_domain_id = 'NODE0000000001'
AND A.qna_node_id = B.node_id
AND A.webview_flag = 'Y'
AND A.delete_flag = 'N'
AND A.qna_id < 'QNAS0000000010'
ORDER BY A.qna_id DESC
)
WHERE rownum = 1
8. 전체게시판 이전글
6.2 EE
이전글 가져올때 전체를 order by
를 한다음 가져온다.
인덱스를 통해서 한건만 읽을려면?
SELECT /*+ ORDERED USE_NL(A B C) INDEX_DESC(A PK_QNA) */ A.*
FROM t_qna A, t_qna_board B,
(
SELECT * FROM t_node START WITH node_id = 'NODE0000000001' CONNECT BY PRIOR node_id
= parent_id) C
WHERE A.qna_id = B.qna_id
AND A.node_id = C.node_id
AND B.webview_flag = 'Y'
AND A.delete_flag = 'N'
AND A.qna_id < 'QNAS0000000010'
AND ROWNUM = 1
8. 전체게시판 이전글 (수정)
6.2 EE
SELECT qna_id
FROM (
SELECT A.qna_id
FROM v_qna_board A, (
SELECT * FROM t_node START WITH node_id = 'NODE0000000001' CONNECT BY PRIOR
node_id = parent_id
) B
WHERE A.qna_domain_id = 'NODE0000000001'
AND A.qna_node_id = B.node_id
AND A.webview_flag = 'Y'
AND A.delete_flag = 'N'
AND A.qna_id > 'QNAS0000000010'
ORDER BY A.qna_id ASC
)
WHERE rownum = 1
9. 전체게시판 다음글
6.2 EE
이전글 가져올때 전체를 order by
를 한다음 가져온다.
인덱스를 통해서 한건만 읽을려면?
SELECT /*+ ORDERED USE_NL(A B C) INDEX(A PK_QNA) */ A.*
FROM t_qna A, t_qna_board B,
(
SELECT * FROM t_node START WITH node_id = 'NODE0000000001' CONNECT BY PRIOR
node_id = parent_id) C
WHERE A.qna_id = B.qna_id
AND A.node_id = C.node_id
AND B.webview_flag = 'Y'
AND A.delete_flag = 'N'
AND A.qna_id > 'QNAS0000000010'
AND ROWNUM = 1
9. 전체게시판 다음글
6.2 EE
V. MSSQL 쿼리
VI. 제품 쿼리 살펴보기
VII. 쿼리 작성 팁
VIII. 에필로그
7.1 ROWNUM vs TOP
7.2 OUTER JOIN
7.3 순환관계
7.1 ROWNUM vs TOP
ROWNUM이란?
테이블 내에 물리적으로 저장되어 있는 컬럼이 아니라 레코드에 번호를 나타내어 주는 필드이다.
ORDER BY, GROUP BY와 같이 사용할 때는 반드시 서브쿼리로 묶어야 정상적인 결과를 가져온다.
SELECT *
FROM TABLE
WHERE ROWNUM < 10
ORDER BY 1
SELECT *
FROM (
SELECT *
FROM TABLE
ORDER BY 1
) A
WHERE ROWNUM < 10
MSSQL에서 TOP은 ORACLE의 ROWNUM과 같이 행의 개수를 제한해서 가져온다.
SQL7.0에서는 조건에 해당하는 모든 데이터를 읽은 후 FULL SORT 방식으로 결과 추출
SQL2000부터는 ‘Top N Engine’은 N개의 데이터를 임시로 저장할 수 있는 개체(버퍼)를 메모리에 생성
한 후 버퍼에 저장된 가장 크거나 작은 값만을 비교 대상으로 처리한다.
SELECT TOP 5 FROM detail ORDER BY quantity DESC
5개의 버퍼를 만들고 테이블 데이터를 순차적으로 읽으면서 이 버퍼를 채운다. 5개의 버퍼가 채워지면 5
번째이후 읽혀진 데이터는 이 버퍼에 저장된 가장 작은 qunatity와 비교된다.
실제 MSSQL에서 TOP.. ORDER BY는 굉장히 빠르다.
그러나 ORACLE의 ROWNUM ORDER BY는 다르다.
실제 쿼리 수행
70만건의 데이터 (0.45초)
SELECT TOP 10 * FROM t_bigtable ORDER BY customer_id desc
TOP이란?
7.1 ROWNUM vs TOP
7.2 OUTER조인
A B
JOIN
2개 이상의 테이블 조인 시 원본 집합은 조인이 발생하
지 않더라도 결과집합에서 나타나도록 하는 것이다.
SELECT *
FROM tab1, tab2
WHERE tab1.col1 = tab2.col1(+)
AND tab1.col2 = ‘A’
SELECT *
FROM tab1 LEFT OUTER JOIN tab2
ON tab1.col1 = tab2.col1
WHERE tab1.col2 = ‘A’
SELECT *
FROM tab1, tab2
WHERE tab1.col1 *= tab2.col1
AND tab1.col2 = ‘A’
TAB1의 조건을 만족하는 건수가 10
건
TAB1과 TAB2의 조인조건을 만족하
는 건수가 5건
그럼 총 결과는 몇 건이 표시?
ANSI
ORACLE
MSSQL
7.2 OUTER조인
SELECT *
FROM t_qna A, t_qna_process B
WHERE A.qna_id = B.qna_id(+)
AND A.created_date LIKE ‘201012%’
AND B.type IN (‘ANEND’, ‘ANADD’)
OUTER 조인을 사용할 때 자주하는 실수입니다.
아래쿼리가 잘못된 이유와 올바른 쿼리로 변경하라.
SELECT *
FROM t_qna A LEFT OUTER JOIN t_qna_process B
ON A.qna_id = B.qna_id
AND B.tpye IN (‘ANEND’, ‘ANADD’)
WHERE A.created_date LIKE ‘201012%’
SELECT *
FROM t_qna A, (
SELECT * FROM t_qna_process B
WHERE type IN (‘ANEND’, ‘ANADD’)
) B
WHERE A.qna_id = B.qna_id(+)
AND A.created_date LIKE ‘201012%’
ANSI ORACLE
SELECT *
FROM t_qna A, t_qna_process B
WHERE A.qna_id = B.qna_id(+)
AND A.created_date LIKE ‘201012%’
AND B.type(+) IN (‘ANEND’, ‘ANADD’)
Type에 (+)을 넣어주면 될까?
IN에는 (+)를 사용할 수 없다.
7.3 순환관계
순환전개란 순환구조를 가진 테이블에서 어떤 점을 시작으로 해서 하위 구조를 전개하는 순전개
(Implosion)이나 역전개(Explosion)을 할때 발생하는 실행계획이다.
SELECT
LPAD(' ', 2*(LEVEL-1)) || node_name
, node_id
FROM t_node
WHERE delete_flag = 'N'
AND depth <= 5
START WITH node_id = 'NODE0000020619'
CONNECT BY PRIOR node_id = parent_id
FILTER
CONNECT BY WITH FILTERING
TABLE ACCESS BY INDEX ROWID OF ‘T_NODE’
INDEX UNIQUE SCAN OF ‘PK_NODE’
NESTED LOOPS
BUFFER SORT
CONNECT BY PUMP
TABLE ACCESS BY INDEX ROWID OF ‘T_NODE’
INDEX RANGE SCAN OF ‘IDX_NODE_PARENT_ID’
TABLE ACCESS FULL OF T_NODE
1. START WITH 조건을 만족하는 모든 로우를 엑세스 하여 버퍼에 저장한다.
①
②
3. 오라클 10.2버젼 이상에서 발생하는 특별한 실행계획
trace를 분석해 봐도 실제 전체 테이블 스캔을 하지 않는 것으로 확인됨.
③
2. PRIOR에 해당하는 값은 버퍼에 저장이 되어있고 parent_id값을 없을때까지 찾아낸다.
WHERE 조건은 가장 나중에 단순한 CHECK기능으로만 작용한다.
ORACLE
7.3 순환관계
SELECT *
FROM t_node
WHERE delete_flag = ‘N’
START WITH node_id =
‘NODE0000000001’
CONNECT BY PRIOR node_id = parent_id
순전개
SELECT *
FROM t_node
WHERE delete_flag = ‘N’
START WITH node_id = ‘NODE0000000001’
CONNECT BY PRIOR parent_id = node_id
역전개
SELECT *
FROM t_node
WHERE delete_flag = ‘N’
START WITH node_id = ‘NODE0000000001’
CONNECT BY PRIOR node_id = parent_id
ORDER SIBLINGS BY sort_order
정렬
5.4.2나 EE에서는 순환관계를 가져올 때 MSSQL의 테이블 리턴 함수를 이용한다.
MSSQL2005부터 지원하는 CTE를 이용하면 순환관계 쿼리를 오라클 처럼 사용할 수 있다.
CTE (Common Table Expression)는?
SELECT쿼리에 기반한 이름이 붙은 임시 결과셋을 지칭한다.
SQL2005부터 지원한다.
[사용법]
WITH CTE이름(column1, column2…)
AS (
SELECT 구분
)
SELECT column1, …
FROM CTE이름
7.3 순환관계
MSSQL
- 스칼라 함수
- 테이블 리턴 함수
스칼라 함수
CREATE FUNCTION 함수명(파라미터)
……
RETURN 데이타타입
사용법 : SELECT 사용자명.func(파라미터) FROM TAB1
MSSQL 사용자정의 함수의 종류
테이블 리턴 함수
CREATE FUNCTION 함수명(파라미터)
……
RETURN 테이블
사용법 : SELECT * FROM func(파라미터)
- EE의 카테고리 계층구조를 CTE를 이용해서 가져오자.
WITH nodeCTE (node_id, node_name, parent_id, depth, sort_order, sort_key)
AS
(
SELECT node_id, node_name, parent_id,depth, sort_order, CAST(sort_order AS VARBINARY(900))
FROM t_node
WHERE node_id = 'NODE0000000001'
UNION ALL
SELECT A.node_id, A.node_name, A.parent_id, A.depth, A.sort_order,
CAST( B.sort_key + CAST ( (A.sort_order+1) AS varbinary(900)) AS varbinary(900))
FROM t_node A INNER JOIN nodeCTE B
ON A.parent_id = B.node_id
)
SELECT REPLICATE(' ', depth) + node_name
FROM nodeCTE
ORDER BY sort_key
순서정렬을 위한 코드
7.3 순환관계
START WITH에 해당
CONNECT BY PRIOR에 해당
에필로그 ­ 다시 정리해볼까요?
1. 실행계획
2. 인덱스 4. 부분범위처리
• SQL작성 후 실행계획을 확인하라.
• SQL 내부 실행과정을 이해하라.
• RBO와 CBO의 차이를 이해하라.
• Optimzer에 영향을 미치는 요소를 알자.
• 실행계획은 읽는 방법을 연습하자.
• 인덱스의 기본개념을 이해하자.
• 인덱스를 탄다고 무조건 빠른것은 아니다.
• 인덱스의 다양한 ACCESS 유형을 알자.
• 인덱스를 타지 않는 유형을 알고 제대로 사용
하자.
• 결합인덱스의 컬럼순서가 중요한 이유는?
3. 조인
• 조인방법에 따로 처리속도가 다르다.
• USE_NL, USE_MERGE, USE_HASH의 기념
개념의 차이를 이해하자.
• SEMI조인과 Filter처리와의 차이를 이해하자.
• 조인방법 및 성능개선을 위해서 고려해야 할
사항들을 알자.
• 전체범위처리와 부분범위처리의 차이를 이해
하자.
• 부분범위처리가 적용될 수 있는 원칙은?
• 부분범위처리를 통해서 수행속도 향상을 위한
여러가지 방법을 적용하자.

More Related Content

What's hot

SQL 튜닝에 Dictionary View 활용하기 Part2_Wh oracle
SQL 튜닝에 Dictionary View 활용하기 Part2_Wh oracleSQL 튜닝에 Dictionary View 활용하기 Part2_Wh oracle
SQL 튜닝에 Dictionary View 활용하기 Part2_Wh oracle엑셈
 
SQL Profile을 이용한 SQL Plan 변경_Wh oracle
SQL Profile을 이용한 SQL Plan 변경_Wh oracleSQL Profile을 이용한 SQL Plan 변경_Wh oracle
SQL Profile을 이용한 SQL Plan 변경_Wh oracle엑셈
 
Fundamentals of Oracle SQL
Fundamentals of Oracle SQLFundamentals of Oracle SQL
Fundamentals of Oracle SQLJAEGEUN YU
 
SQL performance and UDF
SQL performance and UDFSQL performance and UDF
SQL performance and UDFJAEGEUN YU
 
WINDOW FUNCTION의 이해와 활용방법_Wh oracle
WINDOW FUNCTION의 이해와 활용방법_Wh oracleWINDOW FUNCTION의 이해와 활용방법_Wh oracle
WINDOW FUNCTION의 이해와 활용방법_Wh oracle엑셈
 
SQL PlAN MANAGEMENT 활용_Wh oracle
SQL PlAN MANAGEMENT 활용_Wh oracleSQL PlAN MANAGEMENT 활용_Wh oracle
SQL PlAN MANAGEMENT 활용_Wh oracle엑셈
 
대량의 DML 작업에 대한 성능개선방안_Wh oracle
대량의 DML 작업에 대한 성능개선방안_Wh oracle대량의 DML 작업에 대한 성능개선방안_Wh oracle
대량의 DML 작업에 대한 성능개선방안_Wh oracle엑셈
 
효율적인Sql작성방법 2주차
효율적인Sql작성방법 2주차효율적인Sql작성방법 2주차
효율적인Sql작성방법 2주차희동 강
 
효율적인Sql작성방법 3주차
효율적인Sql작성방법 3주차효율적인Sql작성방법 3주차
효율적인Sql작성방법 3주차희동 강
 
ORACLE EXADATA HCC 압축방식 이해하기_Wh oracle
ORACLE EXADATA HCC 압축방식 이해하기_Wh oracleORACLE EXADATA HCC 압축방식 이해하기_Wh oracle
ORACLE EXADATA HCC 압축방식 이해하기_Wh oracle엑셈
 
[2015-06-19] Oracle 성능 최적화 및 품질 고도화 2
[2015-06-19] Oracle 성능 최적화 및 품질 고도화 2[2015-06-19] Oracle 성능 최적화 및 품질 고도화 2
[2015-06-19] Oracle 성능 최적화 및 품질 고도화 2Seok-joon Yun
 
[2015-06-26] Oracle 성능 최적화 및 품질 고도화 3
[2015-06-26] Oracle 성능 최적화 및 품질 고도화 3[2015-06-26] Oracle 성능 최적화 및 품질 고도화 3
[2015-06-26] Oracle 성능 최적화 및 품질 고도화 3Seok-joon Yun
 
SQL쿼리튜닝팁 - 허성
SQL쿼리튜닝팁 - 허성SQL쿼리튜닝팁 - 허성
SQL쿼리튜닝팁 - 허성ETRIBE_STG
 
개발자들이 흔히 실수하는 SQL 7가지
개발자들이 흔히 실수하는 SQL 7가지개발자들이 흔히 실수하는 SQL 7가지
개발자들이 흔히 실수하는 SQL 7가지JungGeun Lee
 
[2015 07-06-윤석준] Oracle 성능 최적화 및 품질 고도화 4
[2015 07-06-윤석준] Oracle 성능 최적화 및 품질 고도화 4[2015 07-06-윤석준] Oracle 성능 최적화 및 품질 고도화 4
[2015 07-06-윤석준] Oracle 성능 최적화 및 품질 고도화 4Seok-joon Yun
 
오라클 커서(Cursor) 개념 및 오라클 메모리 구조_PL/SQL,오라클커서강좌,SGA, PGA, UGA, Shared Pool, Sha...
오라클 커서(Cursor) 개념 및 오라클 메모리 구조_PL/SQL,오라클커서강좌,SGA, PGA, UGA, Shared Pool, Sha...오라클 커서(Cursor) 개념 및 오라클 메모리 구조_PL/SQL,오라클커서강좌,SGA, PGA, UGA, Shared Pool, Sha...
오라클 커서(Cursor) 개념 및 오라클 메모리 구조_PL/SQL,오라클커서강좌,SGA, PGA, UGA, Shared Pool, Sha...탑크리에듀(구로디지털단지역3번출구 2분거리)
 
Commit Wait Class 대기시간 감소 방안_Wh oracle
Commit Wait Class 대기시간 감소 방안_Wh oracleCommit Wait Class 대기시간 감소 방안_Wh oracle
Commit Wait Class 대기시간 감소 방안_Wh oracle엑셈
 
[2015-06-12] Oracle 성능 최적화 및 품질 고도화 1
[2015-06-12] Oracle 성능 최적화 및 품질 고도화 1[2015-06-12] Oracle 성능 최적화 및 품질 고도화 1
[2015-06-12] Oracle 성능 최적화 및 품질 고도화 1Seok-joon Yun
 
효율적인 SQL 작성방법 1주차
효율적인 SQL 작성방법 1주차효율적인 SQL 작성방법 1주차
효율적인 SQL 작성방법 1주차희동 강
 
[2015-07-10-윤석준] Oracle 성능 관리 & v$sysstat
[2015-07-10-윤석준] Oracle 성능 관리 & v$sysstat[2015-07-10-윤석준] Oracle 성능 관리 & v$sysstat
[2015-07-10-윤석준] Oracle 성능 관리 & v$sysstatSeok-joon Yun
 

What's hot (20)

SQL 튜닝에 Dictionary View 활용하기 Part2_Wh oracle
SQL 튜닝에 Dictionary View 활용하기 Part2_Wh oracleSQL 튜닝에 Dictionary View 활용하기 Part2_Wh oracle
SQL 튜닝에 Dictionary View 활용하기 Part2_Wh oracle
 
SQL Profile을 이용한 SQL Plan 변경_Wh oracle
SQL Profile을 이용한 SQL Plan 변경_Wh oracleSQL Profile을 이용한 SQL Plan 변경_Wh oracle
SQL Profile을 이용한 SQL Plan 변경_Wh oracle
 
Fundamentals of Oracle SQL
Fundamentals of Oracle SQLFundamentals of Oracle SQL
Fundamentals of Oracle SQL
 
SQL performance and UDF
SQL performance and UDFSQL performance and UDF
SQL performance and UDF
 
WINDOW FUNCTION의 이해와 활용방법_Wh oracle
WINDOW FUNCTION의 이해와 활용방법_Wh oracleWINDOW FUNCTION의 이해와 활용방법_Wh oracle
WINDOW FUNCTION의 이해와 활용방법_Wh oracle
 
SQL PlAN MANAGEMENT 활용_Wh oracle
SQL PlAN MANAGEMENT 활용_Wh oracleSQL PlAN MANAGEMENT 활용_Wh oracle
SQL PlAN MANAGEMENT 활용_Wh oracle
 
대량의 DML 작업에 대한 성능개선방안_Wh oracle
대량의 DML 작업에 대한 성능개선방안_Wh oracle대량의 DML 작업에 대한 성능개선방안_Wh oracle
대량의 DML 작업에 대한 성능개선방안_Wh oracle
 
효율적인Sql작성방법 2주차
효율적인Sql작성방법 2주차효율적인Sql작성방법 2주차
효율적인Sql작성방법 2주차
 
효율적인Sql작성방법 3주차
효율적인Sql작성방법 3주차효율적인Sql작성방법 3주차
효율적인Sql작성방법 3주차
 
ORACLE EXADATA HCC 압축방식 이해하기_Wh oracle
ORACLE EXADATA HCC 압축방식 이해하기_Wh oracleORACLE EXADATA HCC 압축방식 이해하기_Wh oracle
ORACLE EXADATA HCC 압축방식 이해하기_Wh oracle
 
[2015-06-19] Oracle 성능 최적화 및 품질 고도화 2
[2015-06-19] Oracle 성능 최적화 및 품질 고도화 2[2015-06-19] Oracle 성능 최적화 및 품질 고도화 2
[2015-06-19] Oracle 성능 최적화 및 품질 고도화 2
 
[2015-06-26] Oracle 성능 최적화 및 품질 고도화 3
[2015-06-26] Oracle 성능 최적화 및 품질 고도화 3[2015-06-26] Oracle 성능 최적화 및 품질 고도화 3
[2015-06-26] Oracle 성능 최적화 및 품질 고도화 3
 
SQL쿼리튜닝팁 - 허성
SQL쿼리튜닝팁 - 허성SQL쿼리튜닝팁 - 허성
SQL쿼리튜닝팁 - 허성
 
개발자들이 흔히 실수하는 SQL 7가지
개발자들이 흔히 실수하는 SQL 7가지개발자들이 흔히 실수하는 SQL 7가지
개발자들이 흔히 실수하는 SQL 7가지
 
[2015 07-06-윤석준] Oracle 성능 최적화 및 품질 고도화 4
[2015 07-06-윤석준] Oracle 성능 최적화 및 품질 고도화 4[2015 07-06-윤석준] Oracle 성능 최적화 및 품질 고도화 4
[2015 07-06-윤석준] Oracle 성능 최적화 및 품질 고도화 4
 
오라클 커서(Cursor) 개념 및 오라클 메모리 구조_PL/SQL,오라클커서강좌,SGA, PGA, UGA, Shared Pool, Sha...
오라클 커서(Cursor) 개념 및 오라클 메모리 구조_PL/SQL,오라클커서강좌,SGA, PGA, UGA, Shared Pool, Sha...오라클 커서(Cursor) 개념 및 오라클 메모리 구조_PL/SQL,오라클커서강좌,SGA, PGA, UGA, Shared Pool, Sha...
오라클 커서(Cursor) 개념 및 오라클 메모리 구조_PL/SQL,오라클커서강좌,SGA, PGA, UGA, Shared Pool, Sha...
 
Commit Wait Class 대기시간 감소 방안_Wh oracle
Commit Wait Class 대기시간 감소 방안_Wh oracleCommit Wait Class 대기시간 감소 방안_Wh oracle
Commit Wait Class 대기시간 감소 방안_Wh oracle
 
[2015-06-12] Oracle 성능 최적화 및 품질 고도화 1
[2015-06-12] Oracle 성능 최적화 및 품질 고도화 1[2015-06-12] Oracle 성능 최적화 및 품질 고도화 1
[2015-06-12] Oracle 성능 최적화 및 품질 고도화 1
 
효율적인 SQL 작성방법 1주차
효율적인 SQL 작성방법 1주차효율적인 SQL 작성방법 1주차
효율적인 SQL 작성방법 1주차
 
[2015-07-10-윤석준] Oracle 성능 관리 & v$sysstat
[2015-07-10-윤석준] Oracle 성능 관리 & v$sysstat[2015-07-10-윤석준] Oracle 성능 관리 & v$sysstat
[2015-07-10-윤석준] Oracle 성능 관리 & v$sysstat
 

Similar to Database 튜닝 교육 110124

From MSSQL to MySQL
From MSSQL to MySQLFrom MSSQL to MySQL
From MSSQL to MySQLI Goo Lee
 
실무로 배우는 시스템 성능 최적화 Ch6
실무로 배우는 시스템 성능 최적화 Ch6실무로 배우는 시스템 성능 최적화 Ch6
실무로 배우는 시스템 성능 최적화 Ch6HyeonSeok Choi
 
MySQL_SQL_Tunning_v0.1.3.docx
MySQL_SQL_Tunning_v0.1.3.docxMySQL_SQL_Tunning_v0.1.3.docx
MySQL_SQL_Tunning_v0.1.3.docxNeoClova
 
[오픈소스컨설팅]Day #3 MySQL Monitoring, Trouble Shooting
[오픈소스컨설팅]Day #3 MySQL Monitoring, Trouble Shooting[오픈소스컨설팅]Day #3 MySQL Monitoring, Trouble Shooting
[오픈소스컨설팅]Day #3 MySQL Monitoring, Trouble ShootingJi-Woong Choi
 
Presto User & Admin Guide
Presto User & Admin GuidePresto User & Admin Guide
Presto User & Admin GuideJEONGPHIL HAN
 
보다 빠른 SQL튜닝과 분석을 위한 새로운 툴
보다 빠른 SQL튜닝과 분석을 위한 새로운 툴보다 빠른 SQL튜닝과 분석을 위한 새로운 툴
보다 빠른 SQL튜닝과 분석을 위한 새로운 툴Devgear
 
#25.SQL초보에서 Schema Objects까지_구로IT학원/국비지원IT학원추천/구로디지털단지IT학원/재직자환급교육추천
#25.SQL초보에서 Schema Objects까지_구로IT학원/국비지원IT학원추천/구로디지털단지IT학원/재직자환급교육추천#25.SQL초보에서 Schema Objects까지_구로IT학원/국비지원IT학원추천/구로디지털단지IT학원/재직자환급교육추천
#25.SQL초보에서 Schema Objects까지_구로IT학원/국비지원IT학원추천/구로디지털단지IT학원/재직자환급교육추천탑크리에듀(구로디지털단지역3번출구 2분거리)
 
제12장 시퀀스와 인덱스
제12장 시퀀스와 인덱스제12장 시퀀스와 인덱스
제12장 시퀀스와 인덱스sang doc Lee
 
제1회 Tech Net Sql Server 2005 T Sql Enhancements
제1회 Tech Net Sql Server 2005 T Sql Enhancements제1회 Tech Net Sql Server 2005 T Sql Enhancements
제1회 Tech Net Sql Server 2005 T Sql Enhancementsbeamofhope
 
ecdevday8 웹개발자의 약한고리 SQL 뛰어넘기
ecdevday8 웹개발자의 약한고리 SQL 뛰어넘기ecdevday8 웹개발자의 약한고리 SQL 뛰어넘기
ecdevday8 웹개발자의 약한고리 SQL 뛰어넘기Kenu, GwangNam Heo
 
MySQL_MariaDB-성능개선-202201.pptx
MySQL_MariaDB-성능개선-202201.pptxMySQL_MariaDB-성능개선-202201.pptx
MySQL_MariaDB-성능개선-202201.pptxNeoClova
 
MySQL Performance Tuning (In Korean)
MySQL Performance Tuning (In Korean)MySQL Performance Tuning (In Korean)
MySQL Performance Tuning (In Korean)OracleMySQL
 

Similar to Database 튜닝 교육 110124 (20)

From MSSQL to MySQL
From MSSQL to MySQLFrom MSSQL to MySQL
From MSSQL to MySQL
 
실무로 배우는 시스템 성능 최적화 Ch6
실무로 배우는 시스템 성능 최적화 Ch6실무로 배우는 시스템 성능 최적화 Ch6
실무로 배우는 시스템 성능 최적화 Ch6
 
MySQL_SQL_Tunning_v0.1.3.docx
MySQL_SQL_Tunning_v0.1.3.docxMySQL_SQL_Tunning_v0.1.3.docx
MySQL_SQL_Tunning_v0.1.3.docx
 
1.7 튜닝의도구 sql autorace
1.7 튜닝의도구 sql autorace1.7 튜닝의도구 sql autorace
1.7 튜닝의도구 sql autorace
 
[오픈소스컨설팅]Day #3 MySQL Monitoring, Trouble Shooting
[오픈소스컨설팅]Day #3 MySQL Monitoring, Trouble Shooting[오픈소스컨설팅]Day #3 MySQL Monitoring, Trouble Shooting
[오픈소스컨설팅]Day #3 MySQL Monitoring, Trouble Shooting
 
Presto User & Admin Guide
Presto User & Admin GuidePresto User & Admin Guide
Presto User & Admin Guide
 
2.1 optimizer mode를 변경하는 힌트(rule)
2.1 optimizer mode를 변경하는 힌트(rule)2.1 optimizer mode를 변경하는 힌트(rule)
2.1 optimizer mode를 변경하는 힌트(rule)
 
보다 빠른 SQL튜닝과 분석을 위한 새로운 툴
보다 빠른 SQL튜닝과 분석을 위한 새로운 툴보다 빠른 SQL튜닝과 분석을 위한 새로운 툴
보다 빠른 SQL튜닝과 분석을 위한 새로운 툴
 
#25.SQL초보에서 Schema Objects까지_구로IT학원/국비지원IT학원추천/구로디지털단지IT학원/재직자환급교육추천
#25.SQL초보에서 Schema Objects까지_구로IT학원/국비지원IT학원추천/구로디지털단지IT학원/재직자환급교육추천#25.SQL초보에서 Schema Objects까지_구로IT학원/국비지원IT학원추천/구로디지털단지IT학원/재직자환급교육추천
#25.SQL초보에서 Schema Objects까지_구로IT학원/국비지원IT학원추천/구로디지털단지IT학원/재직자환급교육추천
 
3.4 실행계획 SQL 연산 (Hash Anti-Join)
3.4 실행계획 SQL 연산 (Hash Anti-Join)3.4 실행계획 SQL 연산 (Hash Anti-Join)
3.4 실행계획 SQL 연산 (Hash Anti-Join)
 
3.5 실행계획 SQL 연산 (HASH SEMI-JOIN)
3.5 실행계획 SQL 연산 (HASH SEMI-JOIN)3.5 실행계획 SQL 연산 (HASH SEMI-JOIN)
3.5 실행계획 SQL 연산 (HASH SEMI-JOIN)
 
5.2 비트맵 인덱스
5.2 비트맵 인덱스5.2 비트맵 인덱스
5.2 비트맵 인덱스
 
제12장 시퀀스와 인덱스
제12장 시퀀스와 인덱스제12장 시퀀스와 인덱스
제12장 시퀀스와 인덱스
 
제1회 Tech Net Sql Server 2005 T Sql Enhancements
제1회 Tech Net Sql Server 2005 T Sql Enhancements제1회 Tech Net Sql Server 2005 T Sql Enhancements
제1회 Tech Net Sql Server 2005 T Sql Enhancements
 
ecdevday8 웹개발자의 약한고리 SQL 뛰어넘기
ecdevday8 웹개발자의 약한고리 SQL 뛰어넘기ecdevday8 웹개발자의 약한고리 SQL 뛰어넘기
ecdevday8 웹개발자의 약한고리 SQL 뛰어넘기
 
6.4 hints for access paths(index)
6.4 hints for access paths(index)6.4 hints for access paths(index)
6.4 hints for access paths(index)
 
MySQL_MariaDB-성능개선-202201.pptx
MySQL_MariaDB-성능개선-202201.pptxMySQL_MariaDB-성능개선-202201.pptx
MySQL_MariaDB-성능개선-202201.pptx
 
MySQL Performance Tuning (In Korean)
MySQL Performance Tuning (In Korean)MySQL Performance Tuning (In Korean)
MySQL Performance Tuning (In Korean)
 
1.1 sql문 처리과정
1.1 sql문 처리과정1.1 sql문 처리과정
1.1 sql문 처리과정
 
1.3 dbms stats 패키지사용하기
1.3 dbms stats 패키지사용하기1.3 dbms stats 패키지사용하기
1.3 dbms stats 패키지사용하기
 

More from 한 경만

엘라스틱서치 실무 가이드_202204.pdf
엘라스틱서치 실무 가이드_202204.pdf엘라스틱서치 실무 가이드_202204.pdf
엘라스틱서치 실무 가이드_202204.pdf한 경만
 
아파치 카프카_애플리케이션 프로그래밍 with 자바_202204.pdf
아파치 카프카_애플리케이션 프로그래밍 with 자바_202204.pdf아파치 카프카_애플리케이션 프로그래밍 with 자바_202204.pdf
아파치 카프카_애플리케이션 프로그래밍 with 자바_202204.pdf한 경만
 
도메인 주도 설계 철저 입문_202204.pdf
도메인 주도 설계 철저 입문_202204.pdf도메인 주도 설계 철저 입문_202204.pdf
도메인 주도 설계 철저 입문_202204.pdf한 경만
 
파워포인트 블루스
파워포인트 블루스파워포인트 블루스
파워포인트 블루스한 경만
 
만들면서 배우는 클린 아키텍처
만들면서 배우는 클린 아키텍처만들면서 배우는 클린 아키텍처
만들면서 배우는 클린 아키텍처한 경만
 
개발자 리서치 활동강화 방안 180109
개발자 리서치 활동강화 방안 180109개발자 리서치 활동강화 방안 180109
개발자 리서치 활동강화 방안 180109한 경만
 
소프트웨어 공학의 사실과 오해
소프트웨어 공학의 사실과 오해소프트웨어 공학의 사실과 오해
소프트웨어 공학의 사실과 오해한 경만
 
2018 workshop 180615
2018 workshop 1806152018 workshop 180615
2018 workshop 180615한 경만
 
객체지향의 사실과 오해 7장.함께모으기
객체지향의 사실과 오해 7장.함께모으기 객체지향의 사실과 오해 7장.함께모으기
객체지향의 사실과 오해 7장.함께모으기 한 경만
 
객체지향의 사실과 오해 3장. 타입과 추상화
객체지향의 사실과 오해 3장. 타입과 추상화객체지향의 사실과 오해 3장. 타입과 추상화
객체지향의 사실과 오해 3장. 타입과 추상화한 경만
 
애자일 프랙티스
애자일 프랙티스애자일 프랙티스
애자일 프랙티스한 경만
 
Kafka streams 20201012
Kafka streams 20201012Kafka streams 20201012
Kafka streams 20201012한 경만
 

More from 한 경만 (12)

엘라스틱서치 실무 가이드_202204.pdf
엘라스틱서치 실무 가이드_202204.pdf엘라스틱서치 실무 가이드_202204.pdf
엘라스틱서치 실무 가이드_202204.pdf
 
아파치 카프카_애플리케이션 프로그래밍 with 자바_202204.pdf
아파치 카프카_애플리케이션 프로그래밍 with 자바_202204.pdf아파치 카프카_애플리케이션 프로그래밍 with 자바_202204.pdf
아파치 카프카_애플리케이션 프로그래밍 with 자바_202204.pdf
 
도메인 주도 설계 철저 입문_202204.pdf
도메인 주도 설계 철저 입문_202204.pdf도메인 주도 설계 철저 입문_202204.pdf
도메인 주도 설계 철저 입문_202204.pdf
 
파워포인트 블루스
파워포인트 블루스파워포인트 블루스
파워포인트 블루스
 
만들면서 배우는 클린 아키텍처
만들면서 배우는 클린 아키텍처만들면서 배우는 클린 아키텍처
만들면서 배우는 클린 아키텍처
 
개발자 리서치 활동강화 방안 180109
개발자 리서치 활동강화 방안 180109개발자 리서치 활동강화 방안 180109
개발자 리서치 활동강화 방안 180109
 
소프트웨어 공학의 사실과 오해
소프트웨어 공학의 사실과 오해소프트웨어 공학의 사실과 오해
소프트웨어 공학의 사실과 오해
 
2018 workshop 180615
2018 workshop 1806152018 workshop 180615
2018 workshop 180615
 
객체지향의 사실과 오해 7장.함께모으기
객체지향의 사실과 오해 7장.함께모으기 객체지향의 사실과 오해 7장.함께모으기
객체지향의 사실과 오해 7장.함께모으기
 
객체지향의 사실과 오해 3장. 타입과 추상화
객체지향의 사실과 오해 3장. 타입과 추상화객체지향의 사실과 오해 3장. 타입과 추상화
객체지향의 사실과 오해 3장. 타입과 추상화
 
애자일 프랙티스
애자일 프랙티스애자일 프랙티스
애자일 프랙티스
 
Kafka streams 20201012
Kafka streams 20201012Kafka streams 20201012
Kafka streams 20201012
 

Database 튜닝 교육 110124

  • 1. 데이타베이스 튜닝교육 2011. 01. 24 “SQL 튜닝의 기본개념을 알아 보고 제품에서 활용하자”
  • 2. 전문가들이 하는 DB 튜닝방법은?
  • 8. 최적의 index 및 쿼리 튜닝 작업을 진행
  • 11. 통계화면이 느리다 는데.. 튜닝 한번 해봐~ 저 그런거 안해봤는 데 어떻게 하죠? 개발자 PM
  • 12. 음… 할 사람이 없 으니 우선 한번 해 봐봐 그럼 제가 한번 찾 아서 해볼께요 개발자 PM
  • 13.
  • 14.
  • 16.
  • 17.
  • 20. 풀스캔 타는 쿼리는 무조건 인덱스 추가 해 인덱스는 많이 만 들수록 좋아
  • 21. DBMS버전, 컬럼순서, 업무영향도, 조인형태를 무시 하고 무조건 인덱스를 추가한다.
  • 24.
  • 25. 그렇게 시간만 자꾸 흘러간다...
  • 28. SQL실행의 기본원 리를 공부하세요!! 누구나 SQL을 능숙하게 사용하 고 싶어한다.
  • 29. 업계 최고의 전문가는 아니지만...
  • 32. 데이타베이스 튜닝 관련 서적 “기본개념 습득에 좋은 책”
  • 33. I. SQL 실행계획 II. 인덱스(INDEX) III. 조인 최적화 IV. 부분범위처리 목 차 V. MSSQL 쿼리 VI. eNomix에서 사용하는 쿼리튜닝 VII. 쿼리 작성 팁 VIII. 에필로그
  • 34. 작업 환경설정 오라클 클라이언트 kmhan튜닝실습자료Tools오라클클라이언트_10g 오라클 클라이언트 툴 kmhan튜닝실습자료ToolsSQLTools_pp kmhan튜닝실습자료ToolsSQLDeveloper PLAN_TABLE kmhan튜닝실습자료PLAN_TABLE DB 접속계정 eNomix5.4.2, EE가 설치된 접속계정 ­ MSSQL, ORACLE
  • 35. I. SQL 실행계획 II. 인덱스(index) III. 조인 최적화 IV. 부분범위처리 1.1 내부 실행과정 1.2 Optimizer 1.3 Optimizer에 영향을 미치는 요소 1.4 SQL 실행계획의 이해
  • 36. 1.1 내부 실행과정 SQ L OPTIMIZER Select cols1, col2, col3... From account A, customer B Where A.col1 = B.cols2 AND A.col2 = ‘abc’ SQL 해석 실행 계획 작성 실행 참조 참조 추출 - 사용자는 요구만 하고 Optimizer가 실행계획 수립 - 수립된 실행계획에 따라 엄청난 수행속도 차이 발생 - 실행계획 제어가 어렵다. - Optimzer가 최적의 실행계획을 수립할 수 있도록 종합적이고 전략적인 Factor를 부여 - 비절차형으로 기술해야 함. - 집합적으로 접근해야 함 - SQL이 어떤 역할을 담당하도록 구현 할 것인가? 결과
  • 37. 1.2 Optimizer Optimizer SQL로 요구된 결과를 최적의 성능을 보장하면서 최소의 비용으로 처리할 수 있도록 처리 경로를 결정하는 DBMS의 일부분 CBO vs RBO 1) CBO : 통계정보로부터모든 Access Path 고려하여 실행계획수립 2) RBO : 미리 정해진 Rule에 따라 실행계획수립 Optimizer Mode 1) RULE 2) CHOOSE 3) ALL_ROWS 4) FIRST_ROWS
  • 38. 1.2 Optimizer RBO - Rule Based Optimizer - 여러 개의 실행계획 가운데 가장 높은 순위의 실행계획을 사용 - 수립된 실행계획은 예측 가능 - 분포도를 무시하므로 더 느려질 수 있다. CBO - Cost Based Optimizer - 여러 개의 실행계획 가운데 가장 적은 비용을 가진 실행계획을 사용 - 비용산정 요소로는 SQL형태, Hint, Optimizer Mode, 연산자, Index, Cluster, DBMS용량 등등 - 성능을 최적으로 내기 위해서는 주기적으로 analyze object를 해주어야 한다. 1 Rowid 2 클러스터 조인 3 Unique를 사용한 해시 클러스터 키 4 Unique key 5 클러스터 조인 6 해시 클러스터 키 7 인덱스된 클러스터 키 8 복합컬럼 인덱스 키 9 단일컬럼 인덱스 키 10 범위검색 (Bounded) 11 무계검색 (Unbounded) 12 소트머지 조인 13 최대/최소 인덱스 컬럼 14 Order by 인덱스 컬럼 15 Full table scan
  • 39. 1.3 Optimizer에 영향을 미치는 요소
  • 40. 1.4 SQL 실행계획의 이해 SELECT A.qna_id, A.question_title FROM t_qna A, t_qna_process B WHERE A.qna_id = B.qna_id AND A.spam_flag = 'N‘ AND A.delete_flag = 'N‘ AND A.created_date >= '20101001000000' AND EXISTS ( SELECT 1 FROM t_qna_option WHERE option01 = ‘01023070004' ); ORACLE
  • 41. - PLAN TABLE 생성 [UNIX] SQL> @$ORACLE_HOME/rdbms/admin/utlxplan.sql [WINDOWS] SQL> @C:ORACLEORA9IRDBMSADMINUTLXPLAN.SQL 실행계획 만들기 EXPLAIN PLAN [SET statement_id = ‘description’] [INTO table_name] FOR sql_statement 오라클 실행계획을 볼려면 사용자 계정에 plan_table이 생성되어 있어야 한다.
  • 42. - PLAN TABLE 연습 EXPLAIN PLAN SET statement_id = ‘KMHAN’ INTO plan_table FOR SELECT * FROM t_qna WHERE qna_id = ‘QNA’ 확인 DBMS_XPLAN 패키지 이용 SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY('PLAN_TABLE', 'KMHAN', 'BASIC')) Oracle 9.1부터 지원 BASIC, TYPICAL, SERIAL ­ 실행계획 정보 표시 ALL ­ 실행계획의 모든 항목을 표시 OUTLINE ­ 실행계획을 수립하는데 필요한 힌트목록을 표시 ADVANCED ­ ALL + OUTLINE
  • 43. [연습1] 다음 실행계획의 실행 순서를 말해 보시오. SELECT STATEMENT GOAL CHOOSE NESTED LOOPS TABLE ACCESS FULL OF ‘T_QNA_PROCESS’ INDEX UNIQUE SCAN OF ‘PK_QNA’ 0 1 2 3 EXECUTION PLAN --------------------------------------------- 2 è 3 è 1 è 0 안쪽부터 위에서 아래로
  • 44. [연습2] 다음 실행계획의 실행 순서를 말해 보시오. SELECT STATEMENT GOAL CHOOSE SORT ORDER BY FILTER NESTED LOOPS NESTED LOOPS TABLE ACCESS BY INDEX ROWID OF ‘T_QNA’ INDEX RANGE SCAN OF ‘IDX_QNA_CREATED_DATE_SPAM_DEL’ TABLE ACCESS BY INDEX ROWID OF ‘T_NODE’ INDEX UNIQUE SCAN OF ‘PK_NODE’ INDEX RANGE SCAN OF ‘IDX_QNA_PROCESS_QNA_ID’ TABLLE ACCESS FULL OF ‘T_QNA_OPTION) 0 1 2 3 4 5 6 7 8 9 10 EXECUTION PLAN --------------------------------------------- 6 è 5 è 8 è 7 è 4 è 9 è 3 è 10 è 2 è 1 è 0
  • 45. - AUTOTRACE 실행계획 + 실행통계 정보를 표시해줌 1. set autotrace on è sql을 실제 수행하고 그 결과와 함께 실행계획 및 실행 통계를 출력한다. 2. set autotrace on explain è sql을 실제 수행하고 그 결과와 함께 실행계획을 출력한다. 3. set autotrace on statistics è 실제 수행하고 그 결과와 함께 실행통계를 출력한다 4. set autotrace traceonly è sql을 실제 수행하지만 그 결과는 출력하지 않고 실행계획과 통 계만 출력한다. 5. set autotrace traceonly exlain è sql을 실제 수행하지 않고 실행계획만 출력한다. 6. set autotrace traceonly statistics è sql을 실제 수행하지만 그 결과는 출력하지 않고 실행 통계만을 출력한다. plustrace 롤 : 실행통계를 확인하기 위해서는 v_$sesstat, v_$statname, v_$mystat 뷰를 읽을 권하이 필요 SQL>@?/sqlplus/admin/plustrace.sql SQL>grant plustarce to 계정;
  • 46. - TKPROF 트레이스 파일은 보기가 힘들다 è 트레이스 파일을 보기 좋게 포맷팅 해준다. [트레이스 파일을 분석하는 유틸리티] 1) 실행된 SQL문 2) 구문분석 수행을 위한 CPU 사용시간 3) 디스크로부터 읽은 블록 수 4) 메모리로부터 읽은 블록 수 5) 조건을 만족하는 행 수 6) 옵티마이저의 유형 7) 분석된 실행 계획 8) 실행계획의 비용 계산 결과
  • 47. - TKPROF 실행) tkprof trace파일경로 출력파일 tkprof /oracle/product/10.2.0/db_1/rdbms/log/testdb_ora_14108.trc report.prf call count cpu elapsed disk query current rows --------- --------- -------- ---------- ---------- ---------- ---------- ---------- Parse 12 0.00 0.00 0 0 0 0 Execute 203 0.04 0.08 0 0 0 0 Fetch 239 0.03 0.11 42 703 0 1433 --------- --------- -------- ---------- ---------- ---------- ---------- ---------- total 454 0.08 0.21 42 703 0 1433
  • 48. SQL 실행계획의 이해 SELECT A.qna_id, A.question_title FROM t_qna A, t_qna_process B WHERE A.qna_id = B.qna_id AND A.spam_flag = 'N‘ AND A.delete_flag = 'N‘ AND A.created_date >= '20101001000000' AND EXISTS ( SELECT 1 FROM t_qna_option WHERE option01 = ‘01023070004' ); MSSQL
  • 49. 4. SQL 실행계획의 이해 (MSSQL) MSSQL PLAN_TABLE?? è 필요 없다. 실행계획에 대해서 그래프로 표시해준 다.
  • 50. 4. SQL 실행계획의 이해 (MSSQL) SET SHOWPLAN_TEXT ON SET SHOWPLAN_ALL ON SET STATISTICS PROFILE ON SET STATISTICS IO ON SET STATISTICS TIME ON è 예상 실행계획 표시(StmtText) è 예상 실행계획 표시(모든 정보) è SQL을 실행할때 사용한 실행계획 표시 è 디스크를 읽은 양에 대해 표시 è 쿼리가 걸리는 시간에 대해 표시
  • 51. I. SQL 실행계획 II. 인덱스(index) III. 조인 최적화 IV. 부분범위처리 2.1 인덱스란? 2.2 인덱스의 ACCESS 유형 2.3 인덱스 활용성 방안 2.4 인덱스를 이용한 엑세스 효율 향상 2.5 인덱스의 전략적 구성과 유지
  • 52. 2.1 인덱스란? 인덱스(INDEX)의 특성 - INDEX는 하나 혹은 두개 이상의 컬럼과 ROWID로 구성. - INDEX는 INDEX를 구성하는 컬럼 값으로 정렬되어 있으며 모든 값이 동일할 경우 Physical Address (Oracle => ROWID) 로 정렬되어 있다. - ROWID는 INDEX를 읽고서 테이블을 읽기 위한 몇 가지 정보로 구성되어 있다. - INDEX는 테이블과는 물리적으로 다른 저장장소에 저장될 수 있다. - INDEX가 존재한다고 해서 항상 INDEX를 사용해서 테이블을 액세스 하는 것은 아니다 -Optimizer가 사용여부 결정 - 일반적으로 INDEX는 B-Tree(Balanced Tree)구조를 채택하고 있다. 인덱스(INDEX)의 키(Keys) - Index : DB 내에 실제로 저장되는 물리적 구조체. DDL로 생성,변경,삭제. 빠른 데이터 엑세스 지원. - Key : 논리적 개념. DBMS에서 Integrity Constraint로 구현되고, Index에 의해서 보장되기도 함. 테이블 액세스(Access) 방법 - By User ROWID : 테이블의 특정 Row에 대한 위치정보(ROWID)를 가지고 직접 테이블 액세스 - Full Table Scan : 테이블에 존재하는 모든 Row에 대한 블록의 순차적액세스 - By Index ROWID : 먼저 INDEX를 읽고서 INDEX의 ROWID를 가지고 테이블 Random 액세스
  • 53. - 인덱스의 구조 (B*TREE) 아이디 이름 직급 hgkim 김현곤 대리 jgpark 박정근 부장 kwlee 이경환 사원 hschoi 최학석 대리 ekpark 박은규 부장 shpark 박성후 차장 shnam 남석현 차장 hsyoon 윤현상 대리 ynkim 김용남 사원 yjpark 박유진 대리 SELECT 아이디, 이름, 직급 FROM 스펙트라_직원 WHERE 직급 = ‘대리’ INDEX(직급) TABLE(스펙트라_직원) SORT된 결과 사원 0000A95B.0002.0001 사원 0000A95B.0003.0001 사원 0000A95B.0102.0001 대리 0000A95B.0402.0001 대리 0000A95C.0062.0001 대리 0000A91D.0002.0001 대리 0000A95E.0002.0001 과장 0000A95B.2002.0001 과장 0000A95B.0302.0001 차장 0000A95B.1002.0001 차장 0000A95B.0032.0001 INDEX-KEY ROWID 2.1 인덱스란?
  • 54. 2.2 인덱스의 ACCESS 유형 발생 - 아무런 조건 없이 Table을 읽게 한 경우 - 인덱스가 걸려 있지 않은 컬럼에 대해서 조건주고 Table을 읽게 한 경우 - 인덱스가 걸려 있는 컬럼에 조건을 부여 했을지라도 Optimizer가 Full Table Scan이 유 리하다고 판단한 경우 Access 방식 - 테이블의 첫Row가 들어있는 Block부터 HWM(High Water Mark)까지 읽는다 - 한번에 DB_FILE_MULTIBLOCK_READ_COUNT에서 정한 크기만큼 읽는다 ※ HWM(High Water Mark) ­ 해당 테이블의 데이터가 쓰여진 적이 있는 가 장 마지막 Block - FULL TABLE SCAN
  • 55. - FULL TABLE SCAN 예제 SELECT STATEMENT CHOOSE TABLE ACCESS (FULL) OF T_EMP SELECT * FROM t_emp 결과 14건 SELECT STATEMENT CHOOSE TABLE ACCESS (FULL) OF T_EMP SELECT * FROM t_emp WHERE deptno = 20 결과 5건 실제 읽은 데이터는 동일하다. 2.2 인덱스의 ACCESS 유형
  • 56. 발생 - Unique Index를 구성하고 있는 모든 Key값에 대해서 Equal(=)로 조건이 공급된 경 우 발생한다 Access 방식 - 해당 조건을 만족 하는 값 하나만 읽는다. - INDEX UNIQUE SCAN 2.2 인덱스의 ACCESS 유형
  • 57. SELECT STATEMENT CHOOSE TABLE ACCESS BY INDEX ROWID OF T_EMP INDEX (UNIQUE SCAN) OF PK_EMP (UNIQUE) SELECT * FROM t_emp WHERE empno = 7782 결과 1건 - INDEX UNIQUE SCAN 예제 2.2 인덱스의 ACCESS 유형
  • 58. 발생 - Non-Unique Index를 Access 하는 경우 - Unique Index를 구성하고 있는 컬럼 중 일부 컬럼 에만 값이 공급된 경우 - Unique Index에 Range 조건(like, between, >, <, >=, <=)으로 값이 공급되는 경 우 Access 방식 - 해당 조건을 만족하는 범위+ 아닌 값 하나(1Plus Scan)를 읽게 된다. - Range 조건이 들어온 경우 Index 구성 순서상 이후에 있는 컬럼에 공급된 조건들은 작 업범위를 줄이는데 작용하지 못한다. ※ 1 Plus Scan - 조건을 만족하는 것을 찾았을지라도 다음에 어떤 값이 오는지 모르기 때문에 조건을 만족하지 않는 값 한 건을 더 Access해야만 원하는 범위 를 다 알 수 있다고 판단하여 조건을 만족하지 않는 값 한 건을 더 Access하는 것을 표현한 용어. - INDEX RANGE SCAN 2.2 인덱스의 ACCESS 유형
  • 59. SELECT STATEMENT CHOOSE TABLE ACCESS BY INDEX ROWID OF T_EMP INDEX (RANGE SCAN) OF EMP_IDX01 (NON-UNIQUE) SELECT * FROM t_emp WHERE mgr = 7839 SELECT STATEMENT CHOOSE TABLE ACCESS BY INDEX ROWID OF T_EMP INDEX (RANGE SCAN) OF PK_EMP (UNIQUE) SELECT * FROM t_emp WHERE mgr = 7839 - INDEX RANGE SCAN 예제 2.2 인덱스의 ACCESS 유형
  • 60. 발생 - Optimizer가 Full Table Scan하고 Sort하는 것 보다는 Index Full Scan해 Sort 작업 을 따로 수행하지 않는 것이 유리하다고 판단 한 경우 Access 방식 - 해당 인덱스의 모든 Block을 한번에 한Block씩 순차적으로 읽어 내려간다. (Single Block I/O) - 정렬된 결과를 가져올 수 있다. - INDEX FULL SCAN 2.2 인덱스의 ACCESS 유형
  • 61. SELECT STATEMENT CHOOSE TABLE ACCESS BY INDEX ROWID OF T_EMP INDEX (FULL SCAN) OF EMP_IDX02 (NON-UNIQUE) SELECT /*+ INDEX(A idx_emp_idx02) */ empno, ename, job, hiredate FROM t_emp A WHERE job = 'SALESMAN - INDEX FULL SCAN 예제 2.2 인덱스의 ACCESS 유형
  • 62. 발생 - Where절이나 Select절에 사용된 컬럼이 모두 하나의 인덱스에 구성된 컬럼인 경우 - 결합Index의 경우 최소한한 Column이 NOT Null로 지정되어 있어야 한다 Access 방식 - 인덱스 Leaf Block을 한번에 DB_FILE_MULTIBLOCK_READ_COUNT에서 정한 크기 씩 끝까지 읽어 내려가며 결과값의 Sort가 보장되지 않는다. - Parallel로 수행 가능하다 - Full Table Scan보다 읽어야 할 Block의 수가 적어 유리하다 - INDEX FAST FULL SCAN 2.2 인덱스의 ACCESS 유형
  • 63. SELECT STATEMENT CHOOSE INDEX (FAST FULL SCAN) OF EMP_IDX02 (NON-UNIQUE SELECT /*+ index_ffs(A t_emp_idx02) */ empno, ename, job FROM t_emp A WHERE job = 'SALESMAN' EMP_IDX02 : empno + ename + job - INDEX FAST FULL SCAN 예제 2.2 인덱스의 ACCESS 유형
  • 64. 발생 - 결합인덱스 사용시 선행키 값 없이 인덱스를 사용하는 방법이다. - CBO에서만 사용 가능하다. Access 방식 - 선행칼럼의 인덱스를 조건으로 지정하지 않으면 해당 선행칼럼 조건을 내부적으로 나누 어서 실행된다. - 선행칼럼의 분포도가 좋지 않을수록 빠른 속도를 보장한다. - INDEX SKIP SCAN 2.2 인덱스의 ACCESS 유형
  • 65. SELECT STATEMENT CHOOSE INDEX (SKIP SCAN) OF EMP_IDX02 (NON-UNIQUE) SELECT empno, ename, job FROM t_emp WHERE job = 'SALESMAN' EMP_IDX02 : empno + job - INDEX SKIP SCAN 예제 2.2 인덱스의 ACCESS 유형
  • 66. 발생 - Rowid가 조건으로 공급된 경우 Access 방식 - Rowid를 이용해서 특정 Block의 특정Row를 찾아간다. - 가장 빠른 Access 방식이다. - ROWID 2.2 인덱스의 ACCESS 유형
  • 67. SELECT STATEMENT CHOOSE TABLE ACCESS BY INDEX ROWID OF T_EMP SELECT * FROM t_emp A WHERE rowid = ‘0000A95B.0402.0001’ - ROWID 예제 2.2 인덱스의 ACCESS 유형
  • 68. INDEX 컬럼의 변형 SELECT * FROM DEPT WHERE SUBSTR(DNAME,1,3) = 'ABC' NOT Operator SELECT * FROM EMP WHERE JOB <> 'SALES' NULL, NOT NULL SELECT * FROM EMP WHERE ENAME IS NOT NULL Optimizer의 취사선택 SELECT * FROM EMP WHERE JOB LIKE 'AB%' AND EMPNO = '7890' 2.3 인덱스의 활용성 방안 - 인덱스를 사용하지 않는 경우
  • 69. NULL값은 0이거나 공백이다 ? NULL은 0도 공백도 아니다. è 값을 모른다. 정해지지 않았다는 의미 col = null, col = ‘’ 사용못한다. NULL에 대해서 알아봅시다. SELECT NULL + 1 FROM DUAL의 값은? è NULL - 어떤 값보다 크거나 작지 않다. - 그러므로 어떤 값과 비교 자체가 안된다. - NULL값과의 연산결과는 NULL이다. - 중요한 정보는 NULL대신 DEFAULT처리를 한 다.
  • 70. NULL에 대해서 알아봅시다. ORACLE에서는 NULL값을 인덱스에 저장하지 않는다. SELECT * FROM TABLE WHERE COL1 IS NULL SELECT * FROM TABLE WHERE COL1 IS NOT NULL è 인덱스 사용 불가 MSSQL에서는 NULL값을 인덱스에 저장한다. SELECT * FROM TABLE WHERE COL1 IS NULL SELECT * FROM TABLE WHERE COL1 IS NOT NULL è 인덱스 사용가능
  • 71. NULL의 정렬순서는 어떻게? [ORACLE] 1) A, B, NULL 순으로 2) NULL, A, B 순으로 [MSSQL] 1) NULL, A, B 순으로 2) NULL, A, B 순으로 TAB1의 COL1에 A, B, NULL값이 있다면 1. SELECT COL1 FROM TAB1 ORDER BY COL1 2. SELECT COL1 FROM TAB1 GROUP BY COL1 결과는?
  • 72. SELECT * FROM EMP WHERE ENAME LIKE 'ABC%' SELECT * FROM EMP WHERE SUBSTR(ENAME,1,3) = 'ABC' SELECT * FROM EMP WHERE SAL * 12 = 12000000 SELECT * FROM EMP WHERE TO_CHAR(HIREDATE,'YYMMDD') = '20030101' SELECT * FROM EMP WHERE NVL(COMM,0) < 100 SELECT * FROM EMP WHERE SAL = 12000000 / 12 SELECT * FROM EMP WHERE HIREDATE = TO_DATE('20030101','YYMMDD') ? 2.3 인덱스의 활용성 방안 - 인덱스 컬럼의 변형(External)
  • 73. SELECT * FROM EMP WHERE EMPNO BETWEEN 100 AND 200 AND JOB = 'CLERK' SELECT * FROM EMP WHERE EMPNO BETWEEN 100 AND 200 AND NVL(JOB,'X') = 'CLERK' SELECT * FROM EMP WHERE DEPTNO || JOB = '10SALESMAN' SELECT * FROM EMP WHERE DEPTNO = '10' AND JOB = 'SALSMAN' 2.3 인덱스의 활용성 방안 - 인덱스 컬럼의 변형(External)
  • 74. TABLE ACCESS BY ROWID CHULGOT AND-EQUAL INDEX RANGE SCAN CH_STATUS INDEX RANGE SCAN CH_CUSTNO SELECT custno, chuldate FROM chulgot WHERE custno = ‘DN02’ AND status = ’90’ 4 ROWS 0.51 SEC TABLE ACCESS BY ROWID CHULGOT INDEX RANGE SCAN CH_CUSTNO SELECT custno, chuldate FROM chulgot WHERE custno = ‘DN02’ AND RTRIM(status) = ’90’ 4 ROWS 0.03 SEC SELECT custno, chuldate FROM chulgot WHERE custno LIKE ‘DN%’ AND status LIKE ’9%’ 4 ROWS 0.51 SEC TABLE ACCESS BY ROWID CHULGOT INDEX RANGE SCAN CH_CUSTNO SELECT custno, chuldate FROM chulgot WHERE custno LIKE ‘DN%’ AND RTRIM(status) = ’9%’ 4 ROWS 0.03 SEC TABLE ACCESS BY ROWID CHULGOT INDEX RANGE SCAN CH_STATUS 2.3 인덱스의 활용성 방안 - Suppressing 예제
  • 75. SELECT * FROM SAMPLET WHERE CHA = 10 SELECT * FROM SAMPLET WHERE TO_NUMBER(CHA) = 10 SELECT * FROM SAMPLET WHERE NUM LIKE '200310%' SELECT * FROM SAMPLET WHERE TO_CHAR(NUM) LIKE '200310%' SELECT * FROM SAMPLET WHERE DAT = '01-JAN-2003' SELECT * FROM SAMPLET WHERE DAT = TO_DATE('01-JAN-2003') CREATE TABLE SAMPLET ( CHA CHAR(10), NUM NUMBER (12,3), VAR VARCHAR2(20), DAT DATE) 2.3 인덱스의 활용성 방안 - 인덱스 컬럼의 변형(Internal)
  • 76. SELECT * FROM SAMPLET WHERE VAR = 10 SELECT * FROM SAMPLET WHERE TO_NUMBER(VAR) = 10 SELECT * FROM SAMPLET WHERE NUM = CHA SELECT * FROM SAMPLET WHERE NUM = TO_NUMBER(CHA) SELECT * FROM SAMPLET WHERE DAT = CHA SELECT * FROM SAMPLET WHERE DAT = TO_DATE(CHA) SELECT * FROM SAMPLET WHERE DAT = '01-JAN-2003' SELECT * FROM SAMPLET WHERE DAT = TO_DATE('01-JAN-2003') 2.3 인덱스의 활용성 방안 - 인덱스 컬럼의 변형(Internal)
  • 77. SORT AGGREGATE TABLE ACCESS FULL CHULGOT SELECT SUM(UNCOST) FROM CHULGOT WHERE STATUS = :V1 90이 넘어옴 SELECT SUM(UNCOST) FROM CHULGOT WHERE STATUS = :V2 ‘90’이 넘어옴 SORT AGGREGATE TABLE ACCESS BY ROWID T_QNA INDEX RANGE SCAN CH_STATUS 1 ROW 23.12 SEC 1 ROW 0.12 SEC CHAR 타입 2.3 인덱스의 활용성 방안 - 인덱스 컬럼의 변형(Internal)
  • 78. SELECT ’Not found' INTO :COL1 FROM DUAL WHERE NOT EXISTS ( SELECT '' FROM EMP WHERE EMPNO = '1234') SELECT 'Not found !' INTO :COL1 FROM EMP WHERE EMPNO <> '1234' SELECT * FROM EMP WHERE ENAME LIKE ’천%' AND JOB <> 'SALES' SELECT * FROM EMP a WHERE a.ENAME LIKE ’천%' AND NOT EXISTS ( SELECT '' FROM EMP b WHERE a.Empno = b.Empno AND b.JOB = 'SALES') 2.3 인덱스의 활용성 방안 - Not operator
  • 79. SELECT * FROM EMP WHERE ENAME > ‘ ‘ SELECT * FROM EMP WHERE ENAME IS NOT NULL SELECT * FROM EMP WHERE COMM IS NOT NULL SELECT * FROM EMP WHERE COMM > 0 SELECT * FROM EMP WHERE DEPT_NO IS NULL 2.3 인덱스의 활용성 방안 - NULL & Not NULL
  • 80. Ranking의 차이 SELECT * FROM EMP WHERE ENAME LIKE 'AB%' AND EMPNO = '7890' INDEX Merge 회피 SELECT * FROM EMP WHERE ENAME = 'ABC' AND JOB = 'SAT' Low Cost의 선택 SELECT * FROM EMP WHERE EMPNO > '10' HINT에 의한 선택 SELECT /*+ INDEX(EMP JOB_IDX) */ * FROM EMP WHERE ENAME LIKE 'AB%' AND JOB LIKE 'SA%' empno 인덱스만 사용 - Ename 혹은 job index 중 하 나만 사용 - FULL Scan - Index Merge Full Table Scan job 인덱스만 사용 2.3 인덱스의 활용성 방안 - Optimizer의 취사선택
  • 81. 2.3 인덱스의 활용성 방안 - Rule based vs Cost based SELECT * FROM EMP WHERE JOB = 'SALESMAN' AND EMPNO = '7890' SELECT * FROM EMP WHERE ENAME LIKE 'AB%' AND EMPNO = '7890' SELECT * FROM EMP WHERE ENAME BETWEEN '1101' AND '1210' AND JOB LIKE 'SA%' SELECT * FROM EMP WHERE JOB = 'SALESMAN' AND EMPNO = '7890' SELECT * FROM EMP WHERE ENAME LIKE 'AB%' AND EMPNO = '7890' SELECT * FROM EMP WHERE ENAME LIKE 'AB%' AND JOB LIKE 'SA%' RBO CBO INDEX_MERGE (AND_EQUAL) 항상 EMPNO INDEX만 사용 항상 나중에 생성된 INDEX만 사용 INDEX_MERGE OR 특정 INDEX 분포도에 따라 ENAME INDEX도 사용 분포도에 따라 INDEX 사용
  • 82. - 6 블럭 이상의 테이블에 적용 (6블럭 이하는 연결고리만) - 컬럼의 분포도가 10 ~ 15 % 이내인 경우 적용 (손익분기점) - 분포도가 범위 이내더라도 절대량(약 5,000건 이상)이 많은 경우에는 단일 테이블 클러스터링을 검토할 것(throughput) - 분포도가 범위 이상이더라도 부분범위처리를 목적으로 하는 경우에는 적용 - 인덱스만을 사용하여 요구를 해결하고자 하는 경우는 분포도가 나쁘더라도 적용할 수 있음(손익분기점) 분포도 = 1 / 컬럼값의 종류 X 100 = 컬럼값의 평균 로우수 / 테이블의 총 로우수 X 100 2.3 인덱스의 활용성 방안 - 인덱스의 활용(적용기준)
  • 83. ABC 10 ABC 23 ABC 26 ABC 32 ABC 67 BCA 12 BCA 24 BCA 29 CBA 11 CBA 19 123 7 123 9 123 32 123 36 123 42 123 52 123 56 123 65 123 67 321 13 32 ABC 123 67 ABC 123 SELECT COL1, COL2 FROM TAB1 WHERE COL1 = ‘ABC’ AND COL2 = 123 INDEX(COL1) INDEX(COL2) TABLE(TAB1) 2.4 인덱스를 이용한 엑세스 효율 향상 - INDEX MERGE
  • 84. ABC 111 10 ABC 112 23 ABC 113 26 ABC 123 32 ABC 123 67 BCA 111 12 BCA 112 24 BCA 123 29 CBA 111 11 CBA 112 19 32 ABC 123 67 ABC 123 SELECT COL1, COL2 FROM TAB1 WHERE COL1 = ‘ABC’ AND COL2 = 123 INDEX(COL1, COL2) TABLE(TAB1) 2.4 인덱스를 이용한 엑세스 효율 향상 - 결합 인덱스
  • 85. COL1 COL2 ROWID A 110 10 A 111 11 A 112 5 A 113 18 A 114 54 A 115 23 A 116 12 A 117 53 A 118 22 A 119 14 A 120 41 B 110 15 B 111 25 B 112 62 COL2 COL1 ROWID 110 A X 110 B X 110 C X 110 D X 111 A X 111 B X 111 C X 111 D X 112 A X 112 B X 112 C X 112 D X 113 A X 113 B X SELECT * FROM TAB1 WHERE COL1 = ‘A’ AND COL2 = ‘112’ 2.4 인덱스를 이용한 엑세스 효율 향상 - EQUAL이 결합인덱스에 미치는 영 향 결과는 동일하 다
  • 86. COL1 COL2 ROWID A 110 10 A 111 11 A 112 5 A 113 18 A 114 54 A 115 23 A 116 12 A 117 53 A 118 22 A 119 14 A 120 41 B 110 15 B 111 25 B 112 62 COL2 COL1 ROWID 110 A X 110 B X 111 A X 111 B X 111 C X 111 D X 112 A X 112 B X 112 C X 112 D X 113 A X 113 B X 113 C X 114 A X SELECT * FROM TAB1 WHERE COL1 = ‘A’ AND COL2 BETWEEN ‘111’ AND ‘113’ 2.4 인덱스를 이용한 엑세스 효율 향상 - EQUAL이 결합인덱스에 미치는 영향2
  • 87. COL2 COL1 ROWID 110 D X 111 A X 111 B X 111 C X 111 D X 112 A X 112 B X 112 D X 113 A X SELECT * FROM TAB1 WHERE COL1 = ‘A’ AND COL2 BETWEEN ‘111’ AND ‘112’ COL2 COL1 ROWID 110 D X 111 A X 111 B X 111 C X 111 D X 112 A X 112 B X 112 D X 113 A X SELECT * FROM TAB1 WHERE COL1 = ‘A’ AND COL2 IN (‘112’, ‘111’) TABLE ACCESS BY ROWID TAB1 INDEX RANGE SCAN INDEX1 CONCATENCATION TABLE ACCESS BY ROWID TAB1 INDEX RANGE SCAN INDEX1 TABLE ACCESS BY ROWID TAB1 INDEX RANGE SCAN INDEX1 2.4 인덱스를 이용한 엑세스 효율 향상 - IN을 이용한 엑세스 효율 향상
  • 88. 수학적 의미 A * (B + C) = (A * B) + (A * C) A and (B or C) = (A and B) or (A and C) A = and B in ( ‘1’, ‘3’ ) = (A = and B = ‘1’) or (A = and B = ‘3’) 기하하적 의미 A (선분) G --------------------- COL BETWEEN ‘A’ AND ‘G’ COL IN (‘A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘F’, ‘G’) A B C D E F G 2.4 인덱스를 이용한 엑세스 효율 향상 - IN 연산자의 특성
  • 89. SELECT …………………… FROM TAB1 WHERE AND AND AND AND COL4 > 1000 COL5 = ‘OPER’ COL1 = ‘ABC’ COL2 LIKE ’12%’ COL3 = ‘1234’ COL1 + COL2 + COL3 로 구성된 인덱스 사용 가정 주류 비주류(여당내 야당) 여당 야당 효율화란 ? - 능력 있는 야당을 어떻게 여당으로 영입할 것인가? (인덱스 전략) - 당내 비주류를 어떻게 주류로 끌어들일 것인가? (IN을 활용) COL1 + COL2 + COL3 로 구성된 인덱스 사용 가정 2.4 인덱스를 이용한 엑세스 효율 향상 - SQL내에서 조건들의 역할
  • 90. SELECT …………………… FROM TAB1 WHERE 상품 = ‘PRINTER’ AND AND AND 판매일자 LIKE ‘201001%’ 처리구분 BETWEEN :VAL1 AND :VAL2 COL1 + COL2 + COL3 로 구성된 인덱스 사용 가정 - 상품 + 처리구분 + 판매일자 인덱 스를 사용한다고 가정 - 처리구분은 1, 2, 3, 4로 가정 COL1 + COL2 + COL3 로 구성된 인덱스 사용 가정 - =로 사용되지 않았으므로 판매일 자 조건은 비주류가 됨 - 사용자가 어떤 조건을 부여할지 알 수 없음 SELECT * FROM TAB1 WHERE 상품 = ‘PRINTER’ AND 처리구분 IN (:VAL1, :VAL2, :VAL3, :VAL4) AND 판매일자 LIKE ‘201001%’ 2.4 인덱스를 이용한 엑세스 효율 향상 - IN을 활용한 실행계획 개선방법
  • 91. SELECT …………………… FROM TAB1 WHERE 상품 = ‘PRINTER’ AND AND 판매일자 LIKE ‘201001%’ 부서 LIKE :VAL1 || ‘%’ COL1 + COL2 + COL3 로 구성된 인덱스 사용 가정 상품 + 부서 + 판매일자 인덱스를 사용한다고 가정 SELECT * FROM TAB1 WHERE 상품 = ‘PRINTER’ AND 부서 IN (SELECT 부서 FROM DEPT WHERE 부서 LIKE VAL1) AND 판매일자 LIKE ‘201001%’ 현존하는 테이블을 이용하는 방법 NESTED LOOPS VIEW SORT(UNIQUE) INDEX (RANGE SCAN) OF ‘부서_PK’ TABLE ACCESS (BY ROWID) OF ‘TAB1’ INDEX (RANGE SCAN) OF ‘INDEX1’ - 먼저 서브쿼리가 수행되어 N개의 =을 제공 - 즉, 상품=, 부서=, 판매일자 LIKE 범위가 서브쿼 리 결과만큼 수행됨 - 서브쿼리로 인한 결합처리 실행계획은 이와 같은 형태로 나타남 - 반드시 버스쿼리가 제공자가 될 것 2.4 인덱스를 이용한 엑세스 효율 향상 - 서브쿼리를 이용한 IN조건 추가
  • 92. 서브쿼리, 인라인뷰, 스칼라 서브쿼리? 인라인 뷰 - FROM절 뒤에 오는 서브쿼리 SELECT * FROM TAB1, ( SELECT * FROM TAB2 ) B WHERE A.col1 = B.col1 서브쿼리? 인라인 뷰? 스칼라 서브쿼리? 서브쿼리 - 쿼리 안에 또 다른 SELECT 쿼리블럭 SELECT * FROM TAB1 WHERE col1 IN (SELECT * FROM TAB2) 스칼라 서브쿼리 - SELECT LIST의 서브쿼리 SELECT A.*, (SELECT col2 FROM tab2 WHERE col2 = A.col1) FROM TAB1 A
  • 93. 모조(DUMMY) 테이블을 이용하는 방법 YMD YMD_DATE 20100101 01-JAN-2010 20100102 02-JAN-2010 ……………… ……………… 20110101 01-JAN-2011 ……………… ……………… 20291231 31-DEC-2029 YMD_DUAL YMD YMD_DATE 201001 JAN-2010 201002 FEB-2010 ……………… ……………… 201101 JAN-2011 ……………… ……………… 202912 DEC-2029 YM_DUAL NO NO2 1 01 2 02 ……… ……… 10 10 ……… ……… 99 99 COPY_T - 각테이블의 컬럼마다 UNIQUE 인덱스를 생성해 둘 것 - YMD_DUAL은일자기간을 점으로 만들어 주기 위해 사용 - YM_DUAL은 월별 기간을 점으로 만들어 주기 위해 사용 - COPY_T는 데이터 복제나임의의 값을 생성해 주기 위해 사용 2.4 인덱스를 이용한 엑세스 효율 향상 - 서브쿼리를 이용한 IN 조건 추가
  • 94. SELECT * FROM TAB1 WHERE 상품 = ‘PRINTER’ AND 판매일자 IN (SELECT ymd FROM ymd_dual WHERE ymd BETWEEN ‘20110101 AND ‘20110110’ AND 부서 LIKE :VAL || ‘%’ SELECT * FROM TAB1 WHERE 상품 = ‘PRINTER’ AND 판매일자 BETWEEN ‘20110101’ AND ‘20110110’ AND 부서 LIKE :VAL1 || ‘%’ SELECT * FROM TAB1 WHERE 상품 = ‘PRINTER’ AND 구분 LIKE :type || ‘%’ AND 생산일자 = ‘20110115’ SELECT * FROM TAB1 WHERE 상품 = ‘PRINTER’ AND 구분IN ( SELECT :type || NO FROM copy_t WHERE NO <= DECODE(:type, ‘A’, 10, 15)) AND 생산일자 = ‘20110115’ 상품 + 판매일자 + 부서 인덱스를 사용한다고 가정 상품 + 구분 + 생산일자 인덱스를 사용한다고 가정 구분은 A01, …A10, B01, ...B15… 모조(DUMMY) 테이블을 이용하는 방법 2.4 인덱스를 이용한 엑세스 효율 향상 - 서브쿼리를 이용한 IN 조건 추가
  • 95. 1. 항상 사용하는가? 2. 항상 ‘=‘로 사용되는가? 3. 분포도가 좋은 컬럼 우선 4. SORT 순서는? 5. 어떤 컬럼을 추가? (후보선수 : 예방, 도움) 2.5 인덱스의 전략적 구성과 유지 - 결합인덱스 컬럼 순서 결정
  • 96. 2.5 인덱스의 전략적 구성과 유지 - INDEX 선정 기준 - 분포도가 좋은 컬럼은 단독적으로 생성하여 활용도 향상 - 자주 조합되어 사용되는 경우는 결합인덱스 생성 - 각종 액세스 경우의 수를 만족할수 있도록 인덱스간의 역할분담 - 가능한 수정이 빈번하지 않는 컬럼 - 기본키 및 외부키 (조인의 연결고리가 되는 컬럼) - 결합 인덱스의 컬럼 순서선정에 주의 - 반복수행(loop 내) 되는 조건은 가장 빠른 수행속도를 내게 할 것 - 실제 조사된 액세스 종류를 토대로 선정 및 검증
  • 97. 2.5 인덱스의 전략적 구성과 유지 - INDEX 선정 시 고려사항 - 새로 추가된 인덱스는 기존 액세스 경로에 영향을 미칠 수 있음 - 지나치게 많은 인덱스는 오버헤드를 발생 - 넓은 범위를 인덱스로 처리시 많은 오버헤드 발생 - 옵티마이져를 위한 통계데이타를 주기적으로 갱신 - 인덱스의 개수는 테이블의 사용형태에 따라 적절히 생성 - 분포도가 양호한 컬럼도 처리범위에 따라 분포도가 나빠질 수 있음 - 인덱스 사용원칙을 준수해야 인덱스가 사용되어짐 - 조인(join)시에 인덱스가 사용여부에 주의
  • 98. 2.6 Hint - Hint란? Optimizer가 실행계획을 세워서 쿼리를 실행할 때 모든 것을 Optimizer에게 맡기지 않고 사용자가 원하는 보다 좋은 액세스 경로를 선택할 수 있도록 하는 것이다. - 힌트의 사용법 --+ : 한 라인에 기술할 때 /*+ */ : 여러 라인에 걸쳐 기술할 때 - 사용예제 SELECT * FROM emp WHERE dept_name = ‘개발팀’ è SELECT /*+ INDEX(emp index_dept) */ * FROM emp WHERE dept_name = ‘개발팀’ 사용하려는 인덱스가 없다면? è 힌트를 무시 ORACLE
  • 99. 2.6 Hint Oracle Hint의 종류에 대해서 알아보자 è 자세히 보기 (오라클 사이트) 액세스 수단 선택을 위한 힌트 힌트 내에 정의 된 테이블을 전체 테이블 스캔 FULL 인덱스 범위 스캔에 의한 테이블 액세스를 유도하는 힌트이다. INDEX 인덱스를 경유하여 테이블 액세스할 때 인덱스 컬럼의 내림차순으로 범위 스캔하도록 유도 INDEX_DESC
  • 100. 2.6 Hint 쿼리형태 변형을 위한 힌트 조건절에 OR 혹은 IN연산자 조건을 별도의 실행단위로 분리하여 각각의 최적의 엑세스 경로를 수립 USE_CONCAT 조건절에 있는 OR 혹은 IN 연산자를 연결실행계획으로 처리되지 않도록 사용 USE_CONCAT의 반대개념 NO_EXPAND 서브쿼리와 메인쿼리를 합쳐 조인 형태로 변형하도록 하는 실행계획을 생성하도록 유도 UNNEST 서브쿼리와 메인쿼리를 합쳐 조인 형태로 변형하지 않도록 하는 실행계획을 생성하도록 유도 NO_UNNEST
  • 101. 2.6 Hint 조인 순서 조정을 위한 힌트 FROM절에 기술된 테이블 순서대로 조인하도록 유도 순서만 제시할 뿐 조인 방법과는 무관하므로 USE_NL, USE_MERGE와 같이 사용하는게 일반적 ORDERED 조인 순서를 제어하는 힌트 LEADING
  • 102. 2.6 Hint 조인 방법 조정을 위한 힌트 Nested Loops 방식을 사용하여 조인을 수행하도록 유도하는 힌트이다. USE_NL 해쉬조인 방식으로 조인이 수행되도록 유도하는 힌트 USE_HASH Sort Merge 방식으로 조인이 수행되도록 유도하는 힌트 USE_MERGE
  • 103. 2.6 Hint - 힌트를 사용하는 위치 1. Table 뒤 With를 이용하는 방법 2. JOIN 구문 사이 3. 문장의 마지막 OPTION을 이용하는 방법 - 사용예제 SELECT COL1, COL2 FROM TAB1 A WITH (IDX_TAB1) INNER HASH JOIN TAB2 WITH (NOLOCK) WHERE A.COL3 = ‘1’ OPTION (FORCE ORDER) 사용하려는 인덱스가 없다면? è 오류를 발생 MSSQL 테이블힌 트 조인힌트 쿼리힌트 MSSQL2005에서는 WITH (INDEX(IDX_TAB1)) 으로 사용
  • 104. 2.6 Hint MSSQL Hint의 종류에 대해서 알아보자 è 자세히 보기 (MS 사이트) - 조인 힌트 - 쿼리 힌트 - 테이블 힌트
  • 105. [실습2.1] 인덱스 엑세스 유형 아래 쿼리의 실행계획을 해석하라. SELECT * FROM t_qna FULL TABLE SCAN SELECT * FROM t_qna ORDER BY qna_id INDEX UNIQUE SCAN SELECT * FROM t_qna WHERE delete_flag = 'N' AND created_date >= '20100101000000' AND created_date <= '20110101000000'; INDEX RANGE SCAN
  • 106. [실습2.2] 인덱스 엑세스 유형 아래 쿼리의 실행계획을 해석하라. SELECT /*+ INDEX(A IDX_QNA_ACCOUNT_ANSWER_STATUS) */ account_id, qna_id FROM t_qna A WHERE answer_status = 'ANNOT' AND approval_status = 'APNOT' ORDER BY account_id INDEX FULL SCAN SELECT /*+ INDEX_FFS(A IDX_QNA_ACCOUNT_ANSWER_STATUS) */ account_id FROM t_qna A WHERE answer_status = 'ANNOT' AND approval_status = 'APNOT' ORDER BY account_id INDEX FAST FULL SCAN
  • 107. [실습2.3] 인덱스를 사용 못하는 구조 아래 쿼리에서 인덱스를 사용하는 쿼리로 바꿔라 SELECT * FROM t_qna WHERE domain_id = 'NODE0000000001' AND SUBSTR(answer_status, 0, 3) = 'ANE' SELECT * FROM t_qna WHERE domain_id = 'NODE0000000001' AND answer_status LIKE 'ANE%' SELECT * FROM t_account WHERE role_id <> 'ACCTAGT' SELECT * FROM t_account WHERE role_id IN ('ACCTADM', 'ACCTMGR')
  • 108. [실습2.4] 인덱스를 사용 못하는 구조 SELECT * FROM t_node WHERE alias = 2 SELECT * FROM t_node WHERE alias = ‘2’ 아래 쿼리를 인덱스를 사용하는 쿼리로 바꿔라 데이타타입
  • 109. [실습2.5] 인덱스를 사용 못하는 구조 SELECT 'Not Found' INTO :col1 FROM t_qna WHERE customer_id <> 'rudaks' SELECT 'Not Found' INTO :col1 FROM dual WHERE NOT EXISTS ( SELECT 1 FROM t_qna WHERE customer_id = 'rudaks‘) SELECT * FROM t_qna WHERE node_id = 'NODE0000000001' AND answer_status <> 'ANEND' SELECT * FROM t_qna A WHERE node_id = 'NODE0000000001' AND NOT EXISTS ( SELECT 1 FROM t_qna B WHERE B.qna_id = A.qna_id AND B.ANSWER_STATUS = ANEND' ) 아래 쿼리를 인덱스를 사용하는 쿼리로 바꿔라
  • 110. [실습2.6] 인덱스를 사용 못하는 구조 SELECT * FROM t_qna WHERE delete_flag = 'N' AND answer_date IS NOT NULL SELECT * FROM t_qna WHERE delete_flag = 'N' AND answer_date > ‘ ‘ SELECT * FROM t_qna WHERE NVL(total_feedback, 0) > 50 SELECT * FROM t_qna WHERE total_feedback > 50 아래 쿼리를 인덱스를 사용하는 쿼리로 바꿔라
  • 111. I. SQL 실행계획 II. 인덱스(index) III. 조인 최적화 IV. 부분범위처리 3.1 조인의 개요 3.2 조인의 방법 3.3 Nested Loops Join 3.4 Sort Merge Join 3.5 Hash Join 3.6 Semi Join 3.7 Anti Join 3.8 조인방법 결정 및 성능 개선 팩터 3.9 조인방법의 결정
  • 112. 3.1 조인의 개요 SELECT col1, col2 FROM tab1, tab2 TAB1 COL1 TAB2 COL2 A 가 B 나 C 다 D col1 col2 A 가 B 가 C 가 D 가 A 나 . . . . D 라 조인이란? 한 개 이상의 테이블에서 조건(where)에 해당 되는 데이터를 뽑아내는 것을 말한다. 결과는?
  • 113. Nested Loops Join Sort Merge Join Hash Join 기타 - Semi Join - Anti Join 3.2 조인의 방법
  • 114. X 운 반 단 위 FLD1 = ‘AB’ KEY2 = KEY1 FLD2 = ‘10’ TABLE ACCESS BY ROWID TABLE ACCESS BY ROWID INDEX (FLD1) TAB1 INDEX (KEY2) TAB2 SELECT A.FLD1, … B.FLD1 FROM TAB1 A, TAB2 B WHERE A.KEY1 = B.KEY2 AND A.FLD1 = ‘AB’ AND B.FLD2 = ’10’ - 순차적(부분범위처리 가능) - 종속적 (먼저 처리되는 테이블 의 처리범위에 따라 처리랑 결 정) - 랜덤(Random) 액세스 위주 - 연결고리 상태에 따라 영향이 큼 - 주로 좁은 범위 처리에 유지 3.3 Nested Loops Join - 개요
  • 115. TABLE1 TABLE2 TABLE3 1 A 2 B 3 D 4 K 5 M 6 F 7 E 8 M .... .... A 가 P 나 C 라 H 사 .... E 마 라 10 마 20 (10000 row) (1000 row) (2 row) TABLE3 TABLE2 TABLE1 1 A 2 C 3 D 4 K 5 M 6 F 7 E 8 M .... .... A 가 P 나 C 라 S 마 .... E 마 라 10 마 20 (10000 row) (1000 row) (2 row) 최소 10,000회 이상 ACCESS 최대 6회 이하 ACCESS 3.3 Nested Loops Join - 조인성능결정(1) : Driving
  • 116. TABLE1 TABLE2 TABLE3 1 A 2 B 3 D 4 K 5 M 6 F 7 E 8 M .... .... A 가 P 나 C 라 H 사 .... E 마 라 10 마 20 (10000 row) (1000 row) (2 row) TABLE1 TABLE3 TABLE2 - TABLE1과 두 번째 TABLE2(TABLE3)를 연결하는 일량은 성공한 결과에 관계없이 동 일 - 그러나 연결에 성공한 양은 다음 연결할 일량에 영향을 미침 1 A 2 C 3 D 4 K 5 M 6 F 7 E 8 M .... .... A 가 P 나 C 라 S 마 .... E 마 C 10 E 20 (10000 row) (1000 row) (2 row) 3.3 Nested Loops Join - 조인성능결정(2) : 조인순서
  • 117. 3.3 Nested Loops Join - 조인성능결정(3) : INDEX의 영향 TAB1 TAB2 FLD KEY1 ……… A ……… B ……… C ……… D KEY2 FLD C ……… A ……… G ……… P ……… ① ② 인덱스 있음 인덱스 있음 TAB1 : INDEX SCAN TAB2 : INDEX SCAN TAB2 : INDEX SCAN TAB1 : INDEX SCAN ① ② TAB1 TAB2 FLD KEY1 ……… A ……… B ……… C ……… D KEY2 FLD C ……… A ……… G ……… P ……… ③ ④ 인덱스 있음 인덱스 없음 TAB1 : INDEX SCAN TAB2 : FULL SCAN TAB2 : FULL SCAN TAB1 : INDEX SCAN ③ ④ - 3의 경우 FULL SCAN이 TAB1의 대상 ROW마다 한번씩 실행이 되며, 옵티마이저는 무조건 4의 방법으로 처리가 된다. - 양쪽 모두 인덱스가 없을 경우 SORT_MERGE나 HASH_JOIN으로 처리가 됨
  • 118. SELECT /*+ ORDERED USE_NL(S C P) */ c.channel_desc, p.prod_name, s.* FROM sales_t1 s, channels c, products p WHERE s.channel_id = c.channel_id AND s.prod_id = p.prod_id AND c.channel_class = ‘Indirect’ AND p.prod_category = ‘Boys’ 16467 NESTED LOOPS 297852 NESTED LOOPS 689122 TABLE ACCESS FULL SALES_T1 297852 TABLE ACCESS BY INDEX ROWID CHANNELS 689122 INDEX UNIQUE SCAN CHAN_PK 16469 TABLE ACCESS BY INDEX ROWID PRODUCTS 297852 INDEX UNIQUE SCAN PRODUCTS_PK 16.3 sec SALES CHANNELS PRODUCTS SELECT /*+ ORDERED USE_NL(S P C) */ c.channel_desc, p.prod_name, s.* FROM sales_t1 s, products p , channels c WHERE s.channel_id = c.channel_id AND s.prod_id = p.prod_id AND c.channel_class = ‘Indirect’ AND p.prod_category = ‘Boys’ 16467 NESTED LOOPS 40008 NESTED LOOPS 689122 TABLE ACCESS FULL SALES_T1 40008 TABLE ACCESS BY INDEX ROWID PRODUCTS 689122 INDEX UNIQUE SCAN CHAN_PK 16469 TABLE ACCESS BY INDEX ROWID PRODUCTS 40008 INDEX UNIQUE SCAN PRODUCTS_PK 10.5 sec SALES PRODUCTS CHANNELS 3.3 Nested Loops Join - 조인 순서에 따른 수행속도 차이 예제
  • 119. SELECT /*+ ORDERED USE_NL(Y X) */ CHULNO, CHULDATE, CUSTNAME FROM CUSTOMER Y, CHULGOT X WHERE X.CUSTNO = Y.CUSTNO AND X.CHULDATE = ‘941003’ AND Y.NATION = ‘KOR’ NESTED LOOPS TABLE ACCESS BY ROWID CUSTOMER INDEX RANGE SCAN CU_NATION TABLE ACCESS BY ROWID CHULGOT INDEX RANGE SCAN CH_CUSTNO 1 rows 0.15 sec SELECT /*+ ORDERED USE_NL(X Y) */ CHULNO, CHULDATE, CUSTNAME FROM CHULGOT X, CUSTOMER Y WHERE X.CUSTNO = Y.CUSTNO AND X.CHULDATE = ‘941003’ AND Y.NATION = ‘KOR’ NESTED LOOPS TABLE ACCESS BY ROWID CHULGOT INDEX RANGE SCAN CU_CHULDATE TABLE ACCESS BY ROWID CUSTOMER INDEX RANGE SCAN PK_CUSTNO 1 rows 0.04 sec 3.3 Nested Loops Join - 실행계획에 따른 수행속도 차이 예 제
  • 120. SELECT /*+ ORDERED USE_NL(Y X) */ CHULNO, CHULDATE, CUSTNAME FROM CUSTOMER Y, CHULGOT X WHERE X.CUSTNO = Y.CUSTNO AND X.CHULDATE = ‘941003’ AND Y.NATION = ‘KOR’ NESTED LOOPS TABLE ACCESS BY ROWID CUSTOMER INDEX RANGE SCAN CU_NATION TABLE ACCESS BY ROWID CHULGOT INDEX RANGE SCAN CH_CUSTNO 1 rows 0.15 sec SELECT CHULNO, CHULDATE, CUSTNAME FROM CUSTOMER Y, CHULGOT X WHERE RTRIM(X.CUSTNO) = Y.CUSTNO AND X.CHULDATE = ‘941003’ AND Y.NATION = ‘KOR’ NESTED LOOPS TABLE ACCESS BY ROWID CHULGOT INDEX RANGE SCAN CH_CHULDATE TABLE ACCESS BY ROWID CUSTOMER INDEX RANGE SCAN CH_CUSTNO 1 rows 0.04 sec SELECT CHULNO, CHULDATE, CUSTNAME FROM CUSTOMER Y, CHULGOT X WHERE RTRIM(X.CUSTNO) = RTRIM(Y.CUSTNO) AND X.CHULDATE = ‘941003’ AND Y.NATION = ‘KOR’ rows sec 3.3 Nested Loops Join - Join시 Index의 영향 예제
  • 121. SELECT A1, A2, .., B1, B2, .., C1, C3, .. FROM TAB1 x, TAB2 y, TAB3 Z WHERE x.A1 = y.B1 AND z.C1 = y.B2 AND x.A2 = ’10’ AND y.B2 LIKE ‘B%’ TAB1 TAB2 TAB3 A2 = ’10’ B1 = A1 AND B2 LIKE ‘B%’ C1 = B2 TAB2 TAB3 TAB1 B2 LIKE ‘B%’ C1 = B2 A1 = B1 AND A2 = ’10’ TAB3 TAB2 TAB1 FULL TABLE SCAN B2 = C1 AND B2 LIKE ‘B%’ A1 = B1 AND A2 = ’10’ ...... ........................................ A2 A1 A2 B1 &B 2 C1 TAB2 # B1 # B2 TAB2 # A1 A2 TAB3 # C1 C2 - 상수 값을 받을 수 있어야 액세스 자격이 획득됨(Nested Loops 조인인 경우) - 어떤 액세스 경로가 가장 유리한가? - 인덱스 구조가 어떻게 되어 있는가? TAB1 TAB2 TAB3 3.3 Nested Loops Join - 엑세스 경로의 결정
  • 122. 운 반 단 위 FLD1 = ‘AB’ KEY2 = KEY1 TABLE ACCESS BY ROWID TABLE ACCESS BY ROWID INDEX (FLD1) TAB1 INDEX (KEY2) TAB2 SELECT A.FLD1, … B.FLD1 FROM TAB1 A, TAB2 B WHERE A.KEY1 = B.KEY2 AND A.FLD1 = ‘AB’ AND B.FLD2 = ’10’ b.FLD2 = ’10’ 인 CHECK기능 만 없어짐 b.FLD2 = ’10’이 없어도 일(엑세스)의 량에 별로 영향을 주지 않 음 3.3 Nested Loops Join - Nested Loops Join의 특징
  • 123. - 순차적으로 실행된다. 선행테이블(Driving Table)의 처리범위에 있는 각각의 로우들이 순차 적으로 수행(순차적) - 먼저 액세스 되는 테이블의 처리범위에 의해 처리량이 결정된다. - 나중에 처리되는 테이블은 앞서 처리된 값을 받아 액세스 된다. 자신에게 주어진 상수값만으 로 범위를 줄이는 것이 아니라 이미 가지고 있던 상수값과 제공받은 상수값을 합쳐서 그 중 가 장 유리한 방법으로 연결이 진행된다. - 주로 랜덤 액세스 방식으로 처리된다. - 주어진 조건에 있는 모든 컬럼들이 인덱스를 가지고 있더라도 모두가 사용되는 것은 아니다. - 연결고리가 되는 인덱스에 의해 연결작업이 수행되므로 연결고리 상태가 매우 중요하다. - 부분범위 처리가 가능하다. - 연결작업을 수행한 후 마지막으로 체크되는 조건은 경우에 따라 수행속도에 미치는 영향이 달라진다. 3.3 Nested Loops Join - Nested Loops Join의 특징
  • 124. FLD1 = ‘AB’ FLD2 = ’10’ TABLE ACCESS BY ROWID TABLE ACCESS BY ROWID INDEX (FLD1) TAB1 TAB2 SELECT /*+ USE_MERGE(A B) */ A.FLD1, … B.FLD1 FROM TAB1 A, TAB2 B WHERE A.KEY1 = B.KEY2 AND A.FLD1 = ‘AB’ AND B.FLD2 = ’10’ - 동시적(항상 전체범위 처리) - 독립적 (자기의 처리범위 만 으로 처리량 결정) - 스캔(SCAN)액세스 위주 - 연결고리 상태에 영향 없음 - 주로 넓은 범위 처리에 유리 S O R T S O R T S O R T INDEX (FLD2) 운반단위 A.KEY1 = B.KEY2 를 조건으 로 MERGE 3.4 Sort Merge Join - 개요
  • 125. 두개 이상의 조인 조건이 있으면서 하나 이상의 부등호 조인 조건을 갖는 경우 조인조건은 조인주관조건과 조인체크조건으로 나누어 진다. Join을 위한 Sort Key 컬럼의 정렬순서는 조건 절에 기술된 순서대로 위에서 아래로 순서가 정해짐 SELECT /*+ ORDERED USE_MERGE(T1 T2) */ T1.NO, T1.NO_CH, T2.NO, T2.NO_CH FROM COPY_T1 T1, COPY_T2 T2 WHERE T1.NO = T2.NO AND T1.NO_CH < T2.NO_CH MERGE JOIN SORT JOIN TABLE ACCESS FULL COPY_T1 FILTER SORT JOIN TABLE ACCESS FULL COPY_T2 - 조인주관조건 : T1.NO = T2.NO - 조인체크조건 : T1.NO_CH < T2.NO_CH - 정렬은 T1.NO, T2.NO로 각각 Ascending Sort - T1.NO_CH, T2.NO_CH는 정렬되지 않음 3.4 Sort Merge Join - 조인 연결고리의 역할 비교(1)
  • 126. 두 개 이상의 부등호 조인 조건이 있는 경우 조건 절에 기술된 순서대로 처음 조건이 조인주관조건이 되고 그 이후의 조건들은 조인체크조건이 됨 SELECT /*+ ORDERED USE_MERGE(T1 T2) */ T1.NO, T1.NO_CH, T2.NO, T2.NO_CH FROM COPY_T1 T1, COPY_T2 T2 WHERE T1.NO_CH > T2.NO_CH AND T1.NO < T2.NO MERGE JOIN SORT JOIN TABLE ACCESS FULL COPY_T1 FILTER SORT JOIN TABLE ACCESS FULL COPY_T2 - 조인주관조건 : T1.NO_CH > T2.NO_CH - 조인체크조건 : T1.NO < T2.NO - 정렬은 T1.NO_CH, T2.NO_CH로 각각 Descending Sort - T1.NO, T2.NO는 정렬되지 않음 3.4 Sort Merge Join - 조인 연결고리의 역할 비교(2)
  • 127. FLD1 = ‘AB’ TABLE ACCESS BY ROWID FULL TABLE SCAN INDEX (FLD1) TAB1 TAB2 SELECT /*+ USE_MERGE(A B) */ A.FLD1, … B.FLD1 FROM TAB1 A, TAB2 B WHERE A.KEY1 = B.KEY2 AND A.FLD1 = ‘AB’ AND B.FLD2 = ’10’ S O R T S O R T 운반단위 a.KEY1 = b.KEY2를 조건으로 MERGE . . . . . . . . . . . . . . . . . . . b.FLD2 = ’10’이 없으면 일의 양이 크게 증가 - 조인 컬럼 정렬에 큰 비용 - 정렬 후에는 빠른 결과 보장 - RBO : 인덱스가 없는 경우 - CBO : ALL_ROWS에 최적 (동등 조건에서는 HASH JOIN) 3.4 Sort Merge Join - 특징
  • 128. - 동시적으로 처리된다. 양쪽 집합이 모두 준비가 되어야 머지를 시작할 수 있으므로 순차적인 처리가 불가능하다. - 각 집합이 준비작업을 할 때 다른 집합에서 처리한 결과를 제공받지 않는다. 즉 자신에게 주 어진 상수값에 의해서만 범위를 줄인다. - 정렬 준비가 완료된 후에라야 조인을 시작할 수 있으므로 원초적으로 부분범위처리를 할 수 없어 항상 전체범위처리를 한다. - 주로 스캔방식으로 처리된다. 각자의 처리범위를 줄이기 위해서 인덱스를 사용하는 경우만 랜덤 액세스가 발생함. - 주어진 조건에 있는 모든 컬럼들이 인덱스를 가지고 있더라도 모두 사용되는 것은 아니다. - 조인의 방향과는 거의 무관하다. 3.4 Sort Merge Join - 특징
  • 129. TAB_S TAB_B HASH FUNCTION 1 Build Input 결정 PARTITION TABLE HASH TABLE BITMAP VECTOR 파티션수 결정 P1 C11 C12 P2 P3 C21 C22 C31 C32 C33 HASH AREA ① ② ③ HASH FUNCTION 2 저장할 Partition 결정 Hash value 생성 ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑨ ⑩ ⑩ ⑪ 운반단위 SELECT /*+ USE_HASH(A B) FULL(A) FULL(B) */ FROM A.FLD1, …, B.COL1 FROM TAB_S A, TAB_B B WHERE A.KEY1 = B.KEY2 AND A.FLD1 = ‘AB’ AND B.FLD2 = ’10’ 3.5 Hash Join - 개요
  • 130. 특 징 - 조인 건수가 적을 경우 NL 유리 , 데이터가 대용량 일 경우 Hash 조인 유리 - 순차적(부분범위처리가능) - 연결고리 상태에 영향이 없음 제약사항 HASH_JOIN_ENABLED = FALSE 일 경우 옵티마이저는 hash join을 사용하는 실행계획을 선택 하지 않으려고 하며, 만약 hash 조인을 유도 하고자 할 경우 /*+ use_hash*/ 힌트를 활용 3.5 Hash Join - 특징 및 제약사항
  • 131. - Driving 쪽의처리 범위가 넓어 처리량이 매우 많은경우 - Random Access가 많은경우 - 연결고리 이상이 생겨 비이상적인 실행계획 or 반복 Range Scan이 일어날 경우 - 대상건수가 많고 두 집합간의 크기 차이가 심할 경우 - 대상건수가 많고 두 집합간의 크기 차이가 작을 경우에는 Sort Merge 조인이 유 리 단, 절대량이 많아서 Sort 부하가 아주 크다면 Hash 조인이 더 유리 3.5 Hash Join - Hash Join은 언제 이용하는가?
  • 132. SELECT /*+ ORDERED USE_NL(X Y) */ COUNT(Y.ACC_NM), COUNT(Y.USR_CMPNY), COUNT(X.ACC_CD), . . . . . . FROM AC_MST X, AC_DTL Y WHERE Y.USR_CMPNY = 'TOONI' AND Y.CHNL_GB = 'CH_A' AND Y.ACC_UNT = 'A' AND X.ACC_CD = Y.ACC_CD AND X.YEAR_NO = '07' 1 SORT (AGGREGATE) 63806 NESTED LOOPS 1057 INDEX (RANGE SCAN) OF ‘PK_AC_MST’ (UNIQUE) 63806 TABLE ACCESS (BY INDEX ROWID) OF ‘AC_DTL’ 67380192 INDEX (RANGE SCAN) OF ‘PK_AC_DTL’ (UNIQUE CPU : 195.09 Elapsed : 215.94 - PK_AC_MST : YEAR_NO + ACC_CD - PK_AC_DTL : USR_CMPNY + CHNL_GB + ACC_UNT SELECT /*+ ORDERED USE_HASH(X Y) */ COUNT(Y.ACC_NM), COUNT(Y.USR_CMPNY), COUNT(X.ACC_CD), . . . . . . FROM AC_MST X, AC_DTL Y WHERE Y.USR_CMPNY = 'TOONI' AND Y.CHNL_GB = 'CH_A' AND Y.ACC_UNT = 'A' AND X.ACC_CD = Y.ACC_CD AND X.YEAR_NO = '07' 1 SORT (AGGREGATE) 63806 HASH JOIN 1056 INDEX (RANGE SCAN) OF ‘PK_AC_MST’ (UNIQUE) 63806 TABLE ACCESS (BY INDEX ROWID) OF ‘AC_DTL’ 63807 INDEX (RANGE SCAN) OF ‘PK_AC_DTL’ (UNIQUE) CPU : 0.37 Elapsed : 0.37 3.5 Hash Join - 예제
  • 133. SELECT X.col1, X.col2, …… FROM TAB1 X, TAB2 Y WHERE X.key = Y.key AND other_condition …… 조인 SELECT col1, col2, …… FROM TAB1 WHERE key IN (SELECT key2 FROM TAB2 WHERE condition ……) 세미조인 TAB1 TAB2 조인 세미조인 1 1 1 1 1 M m 1 M 1 m m M M m*m m 관계형태에 따라 전혀 다른 집합 생성 3.6 Semi Join
  • 134. SELECT COL1, COL2 FROM TAB1 X WHERE KEY1 IN (SELECT KEY2 FROM TAB2 Y WHERE COL1 … AND COL2 ……) NESTED LOOPS VIEW SORT (UNIQUE) TABLE ACCESS BY ROWID OF TAB2 INDEX RANGE SCAN OF COL1_IDX TABLE ACCESS BY ROWID OF TAB1 INDEX RANGE SCAN OF KEY1_IDX 조인과 유사한 실행계획 생성 메인쿼리 집합을 보호하기 위해 UNIQUE하게 만듬 제공자 역할 3.6 Semi Join - Nested Loop형 세미조인
  • 135. SELECT 사번, 성명, 직급, 입사일, …… FROM 사원 X Where 부서 = ‘개발팀’ AND 사번 IN (SELECT 사번 FROM 가족 Y WHERE Y.사번 = X.사번 AND Y.생년월일 < ‘19800101’) FILTER TABLE ACCESS BY ROWID OF ‘사원’ INDEX RANGE SCAN OF ‘부서_INDEX’ TABLE ACCESS BY ROWID OF ‘가족’ INDEX RANGE SCAN OF ‘PK_INDEX’ EXISTS와 유사 한 실행계획 조건을 만족하는 첫번째 로우를 만 나면 종료 확인자 역할 메인쿼리의 컬럼이 서브 쿼리에 있으면 서브쿼리 가 먼저 실행될수 없다. 3.6 Semi Join - Nested Loop형 세미조인
  • 136. UPDATE 청구 X SET 입금액 = NVL(입금액, 0) + :IN_AMT WHERE 청구년원 = ‘201101’ AND 고객번호 IN (SELECT 고객번호 FROM 고객 Y WHERE 납입자 = :IN_CUST AND Y.고객번호 = X.고객번호) 고객테이블 : 500만건 (INDEX : 납입자) 청구테이블 : 6000만건 (INDEX : 고객번호 + 청구년월) 고객테이블이 제공자 역할을 할 수가 없으므로 청구테이블 에 대해 매건 500만건을 읽어 야 함. 10000초 UPDATE 청구 X SET 입금액 = NVL(입금액, 0) + :IN_AMT WHERE 청구년원 = ‘201101’ AND 고객번호 IN (SELECT 고객번호 FROM 고객 Y WHERE 납입자 = :IN_CUST) 0.1초 서브쿼리가 먼저 수행이 되어 서 고객테이블은 한번만 읽으 면 됨 3.6 Semi Join - Nested Loop형 세미조인
  • 137. SELECT ………………………… FROM ORDER X WHERE ORDERDATE LIKE ‘201010%’ AND EXISTS (SELECT ‘X’ FROM DEPT Y WHERE Y.DEPTNO = X.SALDEPT AND Y.TYPE1 = ‘1’) FILTER TABLE ACCESS BY ROWID OF ‘ORDER’ INDEX RANGE SCAN OF ‘ORDDATE_INDEX’ TABLE ACCESS BY ROWID OF ‘DEPT’ INDEX RANGE SCAN OF ‘DEPT_PK’ FILTER TABLE ACCESS BY ROWID OF ‘ORDER’ INDEX RANGE SCAN OF ‘ORDDATE_INDEX’ TABLE ACCESS BY ROWID OF ‘DEPT’ INDEX RANGE SCAN OF ‘DEPT_PK’ 3200 3200 3201 10 10 실제로는 DEPT를 10번만 엑 세스 하였다. 어떻게 된 일일까? 3.6 Semi Join - 필터(Filter)형 세미조인
  • 138. 20100930 … 22 20101001 … 11 20101002 … 11 20101001 … 11 20101002 … 11 20101003 … 11 20101001 … 22 20101002 … 22 20101006 … 22 11 1 22 2 11 access 11 1 BUFFER 22 저장 저장 access CHECK CHECK Buffer내용 과 다르면 Access 수행 ORDDATE_INDEX ORDER DEPT_PK DEPT 같으면 Buffer 만 Check하고 완료 새로 액세스한 것은 buffer에 저장 ORDERDATE + SALDEPT로 인덱스가 있다면 DEPT를 액세스 하러 가는 경우가 최소화 22 2 3.6 Semi Join - 필터(Filter)형 세미조인
  • 139. SELECT ………………………… FROM ORDER X WHERE ORDERDATE LIKE ‘201010%’ AND EXISTS (SELECT /*+ HASH_SJ(X, Y) */ ‘X’ FROM DEPT Y WHERE Y.DEPTNO = X.SALDEPT AND Y.TYPE1 = ‘1’) HASH JOIN SEMI TABLE ACCESS BY ROWID OF ‘ORDER’ INDEX RANGE SCAN OF ‘ORDDATE_INDEX’ TABLE ACCESS FULL OF ‘DEPT’ - 연결고리 연산자는 반드시 ‘=‘이 되어야 함 - 서브쿼리 내에 GROUP BY, CONNECT BY, ROWNUM을 사용할 수 없다는 제약이 있음 3.6 Semi Join - 해쉬(Hash)형 세미조인
  • 140. SELECT * FROM CUSTOMERS C WHERE EXISTS (SELECT 'X' FROM SALES_T1 SA WHERE SA.CUST_ID = C.CUST_ID AND AMOUNT_SOLD > 10000 ) 41 FILTER 50000 TABLE ACCESS FULL CUSTOMERS 41 TABLE ACCESS BY INDEX ROWID SALES_T1 2740210 INDEX RANGE SCAN IX_CUST_ID 25초 FILTER로 수행되는 것이 효율적인가? 41 HASH JOIN SEMI 50000 TABLE ACCESS FULL CUSTOMERS 280 TABLE ACCESS FULL SALES_T1 5초 41 FILTER 50000 TABLE ACCESS FULL CUSTOMERS 41 TABLE ACCESS BY INDEX ROWID SALES_T1 2740210 INDEX RANGE SCAN IX_CUST_ID 27초 /*+ HASH_SJ */ /*+ NL_SJ */ 본래는 분산환경에서 분산질의 시 데이터 전송량 감소를 통해 질의를 최적화하기 위한 방법으로 제시 3.6 Semi Join
  • 141. - Filter 수행 시 발생되는 랜덤 Access의 부하를 감소시킨다. - 대용량 데이터 처리시 HASH_SJ, HASH_ANTI를 활용 하는 것이 유리함 (MAIN 집합이 크면 서브쿼리의 수행부하가 커지기 때문에-전제범위 처리 시) ex) 배치 작업, 다량의 데이터 ETL, 데이터 Migration시 데이터 검증시 활용 - NL_SJ, NL_AJ는 Filter 보다 불리 SEMI JOIN의 제약사항 - Subquery 내에서 하나의 Table만 참조 해야함 - Subquery안의 Subquery에는 사용 못함 - Subquery에서의 Main Table과의 연결은 Equal만 가능 - Subquery안에 GROUP BY, CONNECT BY, ROWNUM을 사용하지 못함 - Main SQL에서 조인이 있는 경우는 사용 불가능 - 비용기준 옵티마이져 모드(CBO) 일 때만 사용가능 3.6 Semi Join - Semi Join 활용방안 및 제약사항
  • 142. SELECT /*+ ORDERED USE_HASH( X Y) FULL(Y) NOPARALLEL(Y) */ COUNT(*) FROM STG_CUSTOMER Y WHERE CUSTOMER_ID IN (SELECT /*+ NOPARALLEL(X) FULL(X) */ CUSTOMER_ID FROM IN_CUSTOMER X) 1 SORT AGGREGATE 128448 HASH JOIN 16598176 SORT UNIQUE 16598176 TABLE ACCESS FULL IN_CUSTOMER 128641 TABLE ACCESS FULL STG_CUSTOMER 196.5 sec SELECT /*+ NOPARALLEL(Y) FULL(Y) */ COUNT(*) FROM STG_CUSTOMER Y WHERE CUSTOMER_ID IN (SELECT /*+ NOPARALLEL(X) HASH_SJ(X) */ CUSTOMER_ID FROM IN_CUSTOMER X) 1 SORT AGGREGATE 128448 HASH JOIN SEMI 16598176 SORT UNIQUE 16598176 TABLE ACCESS FULL IN_CUSTOMER 128641 TABLE ACCESS FULL STG_CUSTOMER 45.3 sec * 제공자 역할로 수행한 경우 * 확인자 역할로 수행한 경우(SEMI 조인 방식) 3.6 Semi Join - 사례
  • 143. SELECT COUNT(*) FROM TAB1 WHERE COL1 like ‘ABC%’ AND COL2 NOT IN (SELECT FLD2 FROM TAB2 WHERE FLD3 LIKE ‘1998%’ ) 3.7 Anti Join FILTER TABLE ACCESS BY ROWID OF ‘TAB1’ INDEX RANGE SCAN OF ‘COL1_INDEX’ TABLE ACCESS BY ROWID OF ‘TAB2’ INDEX RANGE SCAN OF ‘FLD3_INDEX’ COL1이 처리범위가 넓다면 서브쿼 리가 다량의 랜덤 액세스가 발생함. SORT_MERGE ANTI 조인으로 유도 HASH ANTI 조인으로 유도
  • 144. 3.7 Anti Join - MERGE ANTI 조인 SELECT COUNT(*) FROM TAB1 WHERE COL1 like ‘ABC%’ AND COL2 IS NOT NULL AND COL2 NOT IN (SELECT /*+ MERGE_AJ */ FLD2 FROM TAB2 WHERE FLD3 LIKE ‘1998%’ AND FLD2 IS NOT NULL ) MERGE JOIN (ANTI) SORT (JOIN) TABLE ACCESS (BY ROWID) OF 'TAB1' INDEX (RANGE SCAN) OF 'COL1_IDX' SORT (UNIQUE) VIEW TABLE ACCESS (BY ROWID) OF 'TAB2' INDEX (RANGE SCAN) OF 'FLD3_IDX' MERGE대상 칼럼이 NOT NULL일 경우 IS NOT NULL 조건 불필요 NOT NULL 지정 NOT NULL 지정
  • 145. 3.7 Anti Join - HASH ANTI 조인 SELECT COUNT(*) FROM TAB1 WHERE COL1 like ‘ABC%’ AND COL2 IS NOT NULL AND COL2 NOT IN (SELECT /*+ HASH_AJ */ FLD2 FROM TAB2 WHERE FLD3 LIKE ‘1998%’ AND FLD2 IS NOT NULL ) HASH JOIN (ANTI) SORT (JOIN) TABLE ACCESS (BY ROWID) OF 'TAB1' INDEX (RANGE SCAN) OF 'COL1_IDX' VIEW TABLE ACCESS (BY ROWID) OF 'TAB2' INDEX (RANGE SCAN) OF 'FLD3_IDX' MERGE대상 칼럼이 NOT NULL일 경우 IS NOT NULL 조건 불필요 NOT NULL 지정 NOT NULL 지정
  • 146. NESTED LOOPS 조인 - Driving 컬럼 조건 - 인덱스 및 연결 고리 정상 여부 - 조인 순서 SORT MERGE 조인 - 가용 메모리 혹은 SORT_AREA_SIZE값 - DB_FILE_MULTIBLOCK_READ_COUNT - Degree of Parallelism HASH 조인 - Build 테이블의 조인대상 집합의 크기 - 가용 메모리 혹은 HASH_AREA_SIZE - DB_FILE_MULTIBLOCK_READ_COUNT - Degree of Parallelism A B JOIN 3.8 조인방법 결정 및 성능 개선 팩터
  • 147. Driving Table 결정 부 분 범 위 처 리 Driving 조건 Nested Loop Join Sort Merge Join Check 조건 Driving과 Check조 건 교환 선행처리 집합으로 부터 값을 받은 경우와 받지 않은 경우를 비교 가능 좁다 넓다 넓다 좁다 불가능 유리 불리 가능 불가능 3.9 조인 방법의 결정
  • 148. [실습3.1] 아래 쿼리의 실행계획을 확인하고 순서대로 설명하라. SELECT ROWNUM rnum, N.qna_id FROM ( SELECT /*+ USE_NL(Q P) */ Q.qna_id FROM t_qna Q, ( SELECT qna_id, MAX(created_date) created_date FROM t_qna_process QP WHERE ((account_id = 'admin1' AND type IN ('ANEND', 'ANADD') ) OR (previous_account_id = 'admin1' AND type = 'ANFWD')) GROUP BY qna_id ) P WHERE Q.qna_id = P.qna_id AND Q.delete_flag = 'N' AND Q.spam_flag='N' AND Q.created_date >= '00000000000000' ORDER BY Q.updated_date DESC, Q.qna_id DESC ) N WHERE ROWNUM <= 10
  • 149. [실습3.2] 아래 쿼리의 실행계획을 확인하고 순서대로 설명하라. SELECT env_id, target_id, target_type, env_value, created_by, created_date, updated_by, updated_date FROM t_env_value E, ( SELECT N.node_id FROM t_node_account_rel R, t_node N WHERE R.node_id = N.node_id AND R.account_id='admin1' AND R.module_type='SVQNA' AND N.node_type='DNODE' ) D WHERE E.target_id = D.node_id AND E.env_id IN ( 'QNA_AGENT_DELETE_USE_FLAG' , 'QNA_AGENT_REROUTE_USE_FLAG' , 'QNA_APPROVAL_USE_FALG' , 'QNA_ANSWER_TITLE_PREFIX' , 'QNA_AGENT_ATTACH_USE_FLAG' ) ORDER BY E.target_id
  • 150. [실습3.3] 아래 쿼리의 실행계획을 확인하고 순서대로 설명하라. SELECT * FROM t_account ACC, ( SELECT A.* FROM ( SELECT A.account_id, COUNT(Q.qna_id) AS not_answer_count FROM t_account A LEFT OUTER JOIN t_qna Q ON A.account_id = Q.account_id AND Q.delete_flag = 'N' AND Q.spam_flag = 'N' AND (Q.answer_status IN ('ANNOT', 'ANFWD', 'ANTMP') OR (Q.answer_status IN ('ANEND') AND Q.approval_status IN ('APREJ', 'APCCL'))) WHERE A.delete_flag = 'N' AND EXISTS ( SELECT 1 FROM t_node_account_rel R, t_node N WHERE R.node_id = N.node_id AND R.account_id = A.account_id AND R.module_type = 'SVQNA' AND N.delete_flag = 'N' ) GROUP BY A.account_id ) A ) A, t_role R WHERE ACC.account_id = A.account_id AND ACC.role_id = R.role_id
  • 151. [실습3.4] 아래 쿼리의 실행계획을 확인하고 순서대로 설명하라. SELECT * FROM t_account A WHERE A.delete_flag = 'N‘ AND A.role_id IN ( 'ACCTMGR','ACCTADM') AND EXISTS ( SELECT 1 FROM t_node_account_rel WHERE account_id = A.account_id AND module_type = 'SVQNA' ) ORDER BY account_id SELECT COUNT(*) FROM t_qna Q WHERE Q.delete_flag = 'N' AND Q.spam_flag='N' AND Q.created_date >= '00000000000000' AND Q.answer_status IN ('ANEND', 'ANADD') AND EXISTS ( SELECT 1 FROM t_qna_process WHERE qna_id = Q.qna_id AND type = 'ANEND' AND created_date LIKE TO_CHAR(SYSDATE, 'YYYYMMDD') || '%' )
  • 152. [실습3.5] 아래 쿼리의 실행계획을 확인하고 순서대로 설명하라. SELECT * FROM t_template T, t_node N WHERE T.delete_flag = 'N' AND T.domain_id = N.node_id AND N.node_type ='DNODE' AND EXISTS ( SELECT /*+ no_unnest */ T.service_type FROM t_node_service S WHERE S.node_id = T.domain_id AND S.service_type = T.service_type AND S.use_flag='Y') AND service_type IN ( 'SVKNW' , 'SVCHT' , 'SVQNA') AND ROWNUM <= 10 no_unnest를 없앤다면?
  • 153. [실습3.6] 아래 쿼리에서 힌트를 사용하여 등록일자 오름차순으로 결과가 나오 도록 쿼리를 변경하라. SELECT Q.* FROM t_node N, t_qna Q, t_qna_process QP WHERE N.node_id = Q.node_id AND Q.qna_id = QP.qna_id AND Q.delete_flag = 'N' AND Q.answer_status IN ('ANNOT', 'ANFWD', 'ANTMP') AND Q.approval_status ='APNOT' AND Q.created_date LIKE '2010%' AND N.delete_flag = 'N' 등록일자 내림차순으로 가져오려면?
  • 154. [실습3.7] 아래 쿼리를 최근 등록일자 순으로 나오게 변경하라. SELECT * FROM ( SELECT ROWNUM RNUM, Q.* FROM t_qna Q WHERE delete_flag = 'N' AND spam_flag='N' AND Q.created_date LIKE '2010%' AND node_id IN ( SELECT /*+ no_unnest */ node_id FROM t_node START WITH node_id='NODE0000000001' CONNECT BY PRIOR node_id=parent_id ) AND ROWNUM <= 10 ORDER BY Q.created_date DESC, Q.rowId ) Q LEFT OUTER JOIN t_qna_option OP ON Q.qna_id = OP.qna_id WHERE rnum >= 1
  • 155. [실습3.8] 아래는 현재 EE에서 사용하는 특정 기간 내에 특정 카테고리 하위 의 qna개수를 가져오는 쿼리이다. EE에서 아래쿼리를 사용하고 있는데 t_qna데이타가 많을 경우 느려질수 있다. 왜 그런가? SELECT count(*) FROM t_qna Q WHERE Q.delete_flag = 'N' AND Q.spam_flag='N' AND Q.created_date >= '20101122000000‘ AND Q.node_id IN ( SELECT /*+ no_unnest */ node_id FROM t_node N START WITH node_id IN ( 'NODE0000000000' ) CONNECT BY PRIOR node_id=parent_id )
  • 156. I. SQL 실행계획 II. 인덱스(index) III. 조인 최적화 IV. 부분범위 처리 4.1 부분범위 처리의 기본개념 4.2 전체범위 vs 부분범위 4.3 부분범위 처리의 적용원칙 4.4 부분범위 처리의 수행속도 향상 원리 4.5 부분범위 처리의 적용원칙
  • 158. 4.2 전체범위처리 vs 부분범위처리 전체 범위 처리 부분 범위 처리 Full Range Scan 후 가공하여 Array Size 만큼 추출 조건을 만족하는 Row 수가 Array Size에 도달되면 멈춤 운반단위 운반단위
  • 159. SELECT SUM(ordqty) FROM order WHERE ord_date LIKE ‘201001%’ GROUP BY ord_dept Select-list절에 sum, count가 있으므로 조건의 일부분만 엑 세스는 불가능 Select-list나 where 절에 그룹함수 사용 하면 부분범위 처리 를 할 수 없다. SELECT * FROM order WHERE ord_date LIKE ‘201001%’ ORDER BY ord_date ORDER BY가 사용 되면 전체범위를 처 리할 수 밖에 없다. ord_date가 선두에 위치한 인덱스가 존 재하면 부분범위 처 리를 할수 있다. 4.3 부분범위처리의 적용원칙 - 부분범위처리의 자격
  • 160. SELECT deptno, emp FROM emp1 WHERE sal > 10000000 UNION SELECT deptno, empno FROM emp2 WHERE hiredate BETWEEN ‘20100101’ AND ‘20101231’ UNION, MINUS, INTERSECT를 사용한 SQL은 부분범위로 처리할 수 없다. 그 결과가 반드시 유일(UNIQUE, DISTINCT) 해야 하기 때문 UNION ALL을 이용 하면 부분범위 처리 가 가능하다. 4.3 부분범위처리의 적용원칙 - 부분범위처리의 자격
  • 161. SELECT * FROM ORDER 0.01 sec SELECT * FROM ORDER ORDER BY item 10 sec SELECT * FROM ORDER WHERE item > ‘ ‘ 0.01 sec SELECT /*+ INDEX(A item_index) * FROM ORDER A WHERE item > ‘ ‘ 0.01 sec 4.4 부분범위처리의 수행속도 향상 원리
  • 162. - 액세스 경로를 이용한 SORT의 대체 - 인덱스만 처리하는 부분범위 처리 - MIN, MAX의 처리 - FILTER형 부분범위 처리 - ROWNUM을 이용한 부분범위 처리 - 인라인뷰를 이용한 부분범위 처리 4.5 부분범위처리의 적용원칙 - 부분범위 처리로의 유도
  • 163. SELECT ord_dept, ordqty * 1000 FROM order WHERE ord_date LIKE ’2009%’ ORDER BY ord_dept DESC 액세스 주관 컬럼은 ord_date이다. ord_dept로 역순정렬을 할려면… SELECT /*+ INDEX_DESC(A ord_dept_index) */ ord_dept, ordqty * 1000 FROM order WHERE ord_date LIKE ’2009%’ AND ord_dept >’ ‘ ord_dept를 역순으로 정렬함으로 써 부분범위 처리 가능 4.5 부분범위처리의 적용원칙 - 엑세스 경로를 이용한 SORT의 대체
  • 164. SELECT ord_date, SUM(qty) FROM order WHERE ord_date LIKE ’2009%’ GROUP BY ord_date ord_date인덱스에 qty를 합친 결 합인덱스 생성하면 인덱스만 사용 함. G R O U P B Y 운반단위 운반단위 G R O U P B Y index (ord_date) table (order) Index (ord_date + qty) 4.5 부분범위처리의 적용원칙 - 인덱스만 액세스 하는 부분범위 처 리
  • 165. SELECT /*+ INDEX_DESC(A pk_order) */ NVL(MAX(seq), 0) + 1 FROM order A WHJERE dept_no = ‘123000’ AND ROWNUM = 1 SELECT MAX(seq) FROM order WHJERE dept_no = ‘123000’ SORT (AGGREGATE) FIRST ROW INDEX RANGE SCAN (MIN/MAX) OF ‘PK_ORDER’ (UNIQUE) 단 한건만 엑세스 하겠다는 의미 오라클 버젼별로 다름 데이터가 없을 경우는 NO DATA FOUND. 그룹함수는 언제나 성공(SUCCESS) 4.5 부분범위처리의 적용원칙 - MIN, MAX의 처리 SELECT MAX(seq) FROM order WHJERE dept_no = ‘123000’ SORT (AGGREGATE) INDEX RANGE SCAN ‘PK_ORDER’ (UNIQUE) 10.2초 0.01초 RBO CBO
  • 166. SELECT NVL(ename, ‘홍길동’) FROM emp WHERE 1=2 결과는? NULL이다 NULL과 그룹함수와 관계에 대해서 알아보자 No rows returned. SELECT COUNT(ename) FROM emp WHERE 1=2 결과는? 0이다 SELECT MAX(ename) FROM emp WHERE 1=2 결과는? NULL이다
  • 167. NVL함수는 한건이라도 데이터가 있는 경우에 적 용이 된다. SELECT NVL(ename, ‘홍길동’) FROM emp WHERE 1=2 결과가 나오게 할려면 어떻게 해야할까? NULL과 그룹함수와 관계에 대해서 알아보자 SELECT NVL(MAX(ename), ‘홍길동’) FROM emp WHERE 1=2 결과는 홍길동이 나온다. 그룹함수는 최소 한건의 데이터는 나온다.
  • 168. Sort (aggregate) 운반단위 운반단위 index (dept) table (item_tab) Index (dept) table (item_tab) SELECT COUNT(*) INTO :CNT FROM ITEM_TAB WHERE DEPT = ‘101’ AND SEQ > 100 …… If CNT > 0 SELECT INTO :CNT FROM DUAL WHERE EXISTS (SELECT ‘X’ FROM ITEM_TAB WHERE DEPT = ‘101’ AND SEQ > 100 …… If CNT > 0 4.5 부분범위처리의 적용원칙 - Filter형 부분범위 처리
  • 169. 4.5 부분범위처리의 적용원칙 - ROWNUM의 활용 운반단위 index (dept) table (item_tab) ① ② ③ ⑩ COUNT (STOPKEY) TABLE ACCESS BY ROWID OF ‘DEPT’ INDEX RANGE SCAN OF ‘PK_DEPT’
  • 170. 4.5 부분범위처리의 적용원칙 - ROWNUM의 활용 운반단위 index (dept) table (item_tab) 1 2 3 4 5 6 7 8 9 10 3 5 1 8 2 6 9 10 4 7 SELECT ROWNUM, item_cd, category_cd, … FROM product WHERE deptno LIKE ‘120%’ And QTY > 0 AND ROWNUM <= 10 ORDER BY item_id
  • 171. 4.5 부분범위처리의 적용원칙 제대로된 결과를 얻을려면 아래의 형식으로 변경해야 한다. SELECT ROWNUM, item_cd, category_cd, … FROM ( SELECT * FROM product WHERE deptno LIKE ‘120%’ AND QTY > 0 ORDER BY item_id ) A WHERE ROWNUM <= 10
  • 172. 4.5 부분범위처리의 적용원칙 - 인라인뷰를 이용한 부분범위처리 SELECT A.dept_name, b.emp_no, B.emp_name, C.sal_ym, C.sal_tot FROM department A, employee B, salary C WHERE B.deptno = A.deptno AND C.empno = B.empno AND A.location = ‘SEOUL’ AND B.job = ‘MANAGER’ AND C.sal_ym = ‘201012’ ORDER BY A.dept_name, B.hire_date, C.sal_ym 서울에 근무하는 매니저들의 입사일 순으로 급 여액 조회
  • 173. 4.5 부분범위처리의 적용원칙 - 인라인뷰를 이용한 부분범위처리 (수정) SELECT /*+ ORDERED USE_NL(X Y) */ A.dept_name, b.emp_no, B.emp_name, C.sal_ym, C.sal_tot FROM ( SELECT A.dept_name, b.emp_no, B.emp_name FROM department A, employee B WHERE B.deptno = A.deptno AND A.location = ‘SEOUL’ AND B.job = ‘MANAGER’ ORDER BY A.dept_name, B.hire_date, C.sal_ym) X, salery Y WHERE Y.empno = X.empno AND Y.sal_ym = ‘201012’ empno + sal_ym 인덱스를 경유 Department, employee는 전체범위 처리를 하지만 salary는 부분범위 처리 가 가능하다.
  • 174. [실습4.1] 답변완료가 된 건들을 최근날짜 순으로 가져오는데 부분범위 처리 가 가능하게 변경하라. SELECT * FROM t_qna A, t_qna_process B WHERE A.qna_id = B.qna_id AND A.delete_flag = 'N' AND A.answer_status = 'ANEND' ORDER BY A.created_date DESC
  • 175. [실습4.2] 처리일자 별 건수를 가져오는 쿼리이다. 부분범위처리가 가능하게 인덱스를 추가해라. SELECT SUBSTR(answer_date, 0, 6), COUNT(*) FROM t_qna WHERE answer_date LIKE '2010%' AND answer_status = 'ANEND' GROUP BY SUBSTR(answer_date, 0, 6)
  • 176. [실습4.3] qna_id의 키를 가져오는 쿼리이다. 부분범위처리를 이용해서 쿼리를 변경하라. SELECT created_key FROM ( SELECT 'QNAS'||LPAD(NVL(MAX(SUBSTR(qna_id,5))+1, '0000000001'), 10, '0') as created_key FROM t_qna WHERE qna_id > '0000000000' ORDER BY qna_id DESC ) WHERE ROWNUM = 1
  • 177. [실습4.4] 사용자 현황별 등록건수를 가져오는 쿼리의 일부분이다. 아래 쿼리는 데이터가 많을 경우 문제가 될 수 있는데 그 원인과 쿼리를 수정하 라. SELECT fn_get_domain_in_account(A.account_id, 'SVQNA', '/') AS domain_names, not_answer_count, today_answer_count, role_id, account_name, login_flag, work_status FROM t_account A, ( SELECT account_id, SUM(not_answer_count) not_answer_count, SUM(today_answer_count) today_answer_count FROM ( SELECT account_id, COUNT(*) not_answer_count, 0 today_answer_count FROM t_qna WHERE delete_flag='N' AND spam_flag='N' AND answer_status IN ('ANNOT', 'ANFWD', 'ANTMP') GROUP BY account_id UNION ALL SELECT account_id, 0 not_answer_count, COUNT(*) today_answer_count FROM t_qna WHERE delete_flag='N' AND spam_flag='N' AND answer_status IN ('ANEND', 'ANADD') AND answer_date >= '20101105' || '000000' AND answer_date <= '20101105' || '999999' GROUP BY account_id ) NT GROUP BY account_id ) S WHERE A.account_id = S.account_id
  • 178. [실습4.5] 미처리된 qna의 최근순으로 10건만 가져오는 쿼리이다. 아래 쿼리 를 부분범위 처리를 이용하여 가져와라. SELECT * FROM ( SELECT ROWNUM rnum, Q.* FROM t_qna Q, t_qna_process B WHERE Q.qna_id = B.qna_id AND spam_flag = 'N' AND delete_flag = 'N' AND (answer_status IN ('ANNOT','ANTMP','ANFWD') OR (answer_status IN ('ANEND', 'ANADD') AND approval_status IN('APREJ','APCCL')) ) AND Q.created_date <= '99991231000000' AND ROWNUM <= 10 ORDER BY Q.created_date DESC ) Q WHERE rnum >= 1
  • 179. V. MSSQL 쿼리 VI. 제품 쿼리 살펴보기 VII. 쿼리 작성 팁 VIII. 에필로그 1.1 인덱스 1.2 조인
  • 180. 5.1 인덱스 클러스터 인덱스 - 인덱스의 리프레벨이 데이타 페이지인 인덱스로서 데이타 페이지의 행들은 인덱스 키 값의 순으로 정렬되어 있다. - UNIQUE 키워드와 함께 만들어지고 키 값들이 고유한 값을 가지도록 되어있다. - 일반적으로 별도로 지정하지 않으면 Primary Key가 Cluster Index로 생성이 된다. - 테이블당 1개의 Cluster Index만 만들어진다. 인덱스 페이지 데이타 페이지 비잎 수준 잎 수준
  • 181. 5.1 인덱스 넌클러스터 인덱스 - 데이타 페이지는 정렬되어 있지 않고 리프레벨 인덱스 페이지에서 각 행의 ID를 가 진다. - 테이블당 여러개의 NonCluster Index가 생성이 된다. 인덱스 페이지 데이타 페이지 비잎 수준 잎 수준 잎 수준 (키 값)
  • 182. 5.1 인덱스 - 인덱스 정보보기 SP_HELPINDEX 테이블명 - 인덱스 생성 CREATE INDEX 인덱스명 ON 테이블명(컬럼명, ...) WITH DROP_EXISTING
  • 183. 5.1 인덱스 복합인덱스는 둘 이상의 컬럼으로 만드는 인덱스이다. 복합인덱스 쿼리 검색에 필요한 모든 컬럼들이 포함된(넌클러스터) 인덱스를 말한다. 인덱스 검색 후 북마크 룩업을 해야 하는데 필요한 모든 컬럼 값이 인덱스에 이미 다 있으므로 북마크 룩업 과정은 생략된다. 커버된 인덱스 둘 이상의 인덱스를 동시에 사용하여 인덱스 교집합을 만든다. 인덱스 교집합
  • 184. 5.2 조인 중첩루프(Nested Loop) 조인 병합(Merge) 조인 해쉬(Merge) 조인
  • 185. V. MSSQL 쿼리 VI. 제품 쿼리 살펴보기 VII. 쿼리 작성 팁 VIII. 에필로그 1.1 eNomix 5.4.2 1.2 EE
  • 186. 6.1 eNomix 5.4.2 SELECT * FROM ( SELECT /*+ INDEX_DESC(N2 idx_noticeboard_board) */ * FROM ( SELECT /*+ ORDERED */ ROWNUM rseq, N.* FROM t_noticeboard N, t_ticketbox T WHERE T.tb_id = N.tbox_id AND N.ref > 0 AND N.register_date >= '20000101000000' AND N.register_date <= '20201231000000' AND RTRIM(N.tbox_id) IN ( SELECT tb_id FROM t_ticketbox WHERE tb_parent_id = 'TBOXCATEGORIESPOOL' ) AND N.flag_delete = 'N' AND ROWNUM <= 10 ) N2 ) N3 WHERE rseq >= 0 1. 공개 게시판 인덱스 ref DESC + step 오라클 버젼별로 순서가 맞지 않게 나오는 문제가 있음 Ref순으로 나오게 할려면 어떻게 변경을 해야 할까? url : devora.spectra.co.kr sid : devora Id/pwd : mail_lotte/mail_lotte
  • 187. 6.1 eNomix 5.4.2 SELECT * FROM ( SELECT /*+ USE_NL(N T) INDEX(N idx_noticeboard_board) */ ROWNUM rseq, N.* FROM t_noticeboard N, ( SELECT tb_id FROM t_ticketbox WHERE tb_parent_id = 'TBOXCATEGORIESPOOL' ) T WHERE T.tb_id = N.tbox_id AND N.ref > 0 AND N.register_date >= '20000101000000' AND N.register_date <= '20201231000000' AND N.flag_delete = 'N' AND ROWNUM <= 10 ) A WHERE rseq >= 0 1. 공개 게시판(수정) 인덱스 ref DESC + step
  • 188. SELECT * FROM ( SELECT /*+ INDEX_DESC(N2 idx_noticeboard_board) */ * FROM ( SELECT /*+ ORDERED */ ROWNUM rseq, N.* FROM t_noticeboard N, t_ticketbox T“ WHERE T.tb_id = N.tbox_id AND N.ref > 0 AND N.member_id = ‘test’ AND N.register_date >= '20000101000000' AND N.register_date <= '20201231000000' AND RTRIM(N.tbox_id) IN ( SELECT tb_id FROM t_ticketbox WHERE tb_parent_id = ‘'TBOXCATEGORIESPOOL’ ) AND N.flag_delete = 'N‘ AND ROWNUM <= 10 ) N2 ) N3 WHERE rseq >= 0 2. 나의 문의 내용보기 member_id에 대한 인덱스가 없음. Ref순으로 나오지 않음 Member_id에 대해서 ref순으로 나오게 할려면? 인덱스 ref DESC + step 6.1 eNomix 5.4.2
  • 189. SELECT * FROM ( SELECT /*+ USE_NL(N T) INDEX(N idx_noticeboard_member_board) */ ROWNUM rseq, N.* FROM t_noticeboard N, ( SELECT tb_id FROM t_ticketbox WHERE tb_parent_id = 'TBOXCATEGORIESPOOL' ) T WHERE T.tb_id = N.tbox_id AND N.ref > 0 AND N.register_date >= '20000101000000' AND N.register_date <= '20201231000000' AND N.flag_delete = 'N' AND ROWNUM <= 10 ) A WHERE rseq >= 0 2. 나의 문의 내용보기 (수정) member_id에 대한 인덱스가 없음. 날짜순으로 나오지 않음 member_id, ref DESC, step 로 인덱스를 생성 6.1 eNomix 5.4.2
  • 190. SELECT * FROM t_ticketbox START WITH tb_id = 'TBOXCATEGORIESPOOL' CONNECT BY PRIOR tb_id = tb_parent_id 3. 카테고리 정보 가져오기 6.1 eNomix 5.4.2 위의 쿼리를 실행시키면 실행이 안된다. 어떻게 수정을 해야할까?
  • 191. SELECT * FROM ( SELECT * FROM t_ticketbox WHERE tb_id < 'TBOXCATEGORIESPOOL' ) A START WITH tb_parent_id = 'TBOXCATEGORIESPOOL' CONNECT BY PRIOR tb_id = tb_parent_id 3. 카테고리 정보 가져오기 (수정) 6.1 eNomix 5.4.2 TBOXCATEGORIESPOOL은 tb_id와 tb_parent_id가 같 아서 무한루프를 돈다.
  • 192. SELECT * FROM ( SELECT rownum ROW_NUM, BL.* FROM ( SELECT T4.question_id, T4.question_body, T2.category_id, T3.category_name,T3.parent_id, T1.frequency counts FROM t_question T1, t_answer T2 , t_category T3, t_user_top_n_01 T4 WHERE T4.question_id = T1.question_id AND T1.answer_id = T2.answer_id AND T2.category_id = T3.category_id AND T3.web_view ='VIEW00' AND T3.flag_delete='NOTDEL' AND T2.web_view = 'VIEW00' AND T2.flag_delete='NOTDEL' AND T1.question_type <> 'TY_RAR' AND T1.flag_delete='NOTDEL' ORDER BY T4.sort_order ) BL ) WHERE ROW_NUM <= 10 4. TOP-N 가져오기 6.1 eNomix 5.4.2 FAQ건수가 5만건 이라면 어떤 인덱스를 추가해야 하고 쿼리를 어떻게 변경을 해야 할까?
  • 193. 작성필요 4. TOP-N 가져오기 6.1 eNomix 5.4.2 FAQ건수가 5만건 이라면 어떤 인덱스를 추가해야 하고 쿼리를 어떻게 변경을 해야 할까?
  • 194. SELECT A.* FROM ( SELECT ROWNUM rnum, A.* FROM ( SELECT A.* FROM v_qna_board A, ( SELECT * FROM t_node START WITH node_id = 'NODE0000000001' CONNECT BY PRIOR nod e_id = parent_id) C WHERE A.board_node_id = C.node_id AND A.webview_flag = 'Y' AND A.delete_flag = 'N' AND A.customer_id = '1' ORDER BY A.created_date DESC ) A WHERE ROWNUM <= 10 ) A WHERE rnum >= 1 6. 나의 문의 리스트 6.2 EE 회원별 전체 데이터를 정렬을 한 다음 10 건을 가져온다. 인덱스를 통한 10건만 엑세스 할려면 어 떻게 변경을 해야할까?
  • 195. SELECT A.* FROM ( SELECT ROWNUM rnum, A.* FROM ( SELECT /*+ USE_NL(A C) INDEX_DESC(A IDX_QNA_CUSTOMER_ID_DATE) */ A.* FROM t_qna A, t_qna_board B, ( SELECT * FROM t_node START WITH node_id = 'NODE0000000001' CONNECT BY PRIOR node_id = parent_id) C WHERE A.qna_id = B.qna_id AND A.node_id = C.node_id AND B.webview_flag = 'Y' AND A.delete_flag = 'N' AND A.customer_id = '1' AND A.created_date > '00000000000000' ) A WHERE ROWNUM <= 10 ) A WHERE rnum >= 1 6.2 EE 인덱스 추가해야 함. customer_id, created_date 6. 나의 문의 리스트
  • 196. SELECT A.* FROM ( SELECT ROWNUM rnum, A.* FROM ( SELECT A.* FROM v_qna_board A, ( SELECT * FROM t_node START WITH node_id = 'NODE0000000001' CONNECT BY PRIOR node_id = parent_id) C WHERE A.board_node_id = C.node_id AND A.webview_flag = 'Y' AND A.delete_flag = 'N' ORDER BY A.created_date DESC ) A WHERE ROWNUM <= 10 ) A WHERE rnum >= 1 7. 전체 게시판이라면 6.2 EE
  • 197. SELECT A.* FROM ( SELECT ROWNUM rnum, A.* FROM ( SELECT /*+ USE_NL(A B C) INDEX_DESC(A IDX_QNA_CRE_DATE_ASC_SPAM_DEL) */ A.* FROM t_qna A, t_qna_board B, ( SELECT * FROM t_node START WITH node_id = 'NODE0000000001' CONNECT BY PRIOR node_id = parent_id) C WHERE A.qna_id = B.qna_id AND A.node_id = C.node_id AND B.webview_flag = 'Y' AND A.delete_flag = 'N' AND A.created_date > '00000000000000' ) A WHERE ROWNUM <= 10 ) A WHERE rnum >= 1 7. 전체 게시판이라면 6.2 EE
  • 198. SELECT qna_id FROM ( SELECT A.qna_id FROM v_qna_board A, ( SELECT * FROM t_node START WITH node_id = 'NODE0000000001' CONNECT BY PRIOR node_id = parent_id ) B WHERE A.qna_domain_id = 'NODE0000000001' AND A.qna_node_id = B.node_id AND A.webview_flag = 'Y' AND A.delete_flag = 'N' AND A.qna_id < 'QNAS0000000010' ORDER BY A.qna_id DESC ) WHERE rownum = 1 8. 전체게시판 이전글 6.2 EE 이전글 가져올때 전체를 order by 를 한다음 가져온다. 인덱스를 통해서 한건만 읽을려면?
  • 199. SELECT /*+ ORDERED USE_NL(A B C) INDEX_DESC(A PK_QNA) */ A.* FROM t_qna A, t_qna_board B, ( SELECT * FROM t_node START WITH node_id = 'NODE0000000001' CONNECT BY PRIOR node_id = parent_id) C WHERE A.qna_id = B.qna_id AND A.node_id = C.node_id AND B.webview_flag = 'Y' AND A.delete_flag = 'N' AND A.qna_id < 'QNAS0000000010' AND ROWNUM = 1 8. 전체게시판 이전글 (수정) 6.2 EE
  • 200. SELECT qna_id FROM ( SELECT A.qna_id FROM v_qna_board A, ( SELECT * FROM t_node START WITH node_id = 'NODE0000000001' CONNECT BY PRIOR node_id = parent_id ) B WHERE A.qna_domain_id = 'NODE0000000001' AND A.qna_node_id = B.node_id AND A.webview_flag = 'Y' AND A.delete_flag = 'N' AND A.qna_id > 'QNAS0000000010' ORDER BY A.qna_id ASC ) WHERE rownum = 1 9. 전체게시판 다음글 6.2 EE 이전글 가져올때 전체를 order by 를 한다음 가져온다. 인덱스를 통해서 한건만 읽을려면?
  • 201. SELECT /*+ ORDERED USE_NL(A B C) INDEX(A PK_QNA) */ A.* FROM t_qna A, t_qna_board B, ( SELECT * FROM t_node START WITH node_id = 'NODE0000000001' CONNECT BY PRIOR node_id = parent_id) C WHERE A.qna_id = B.qna_id AND A.node_id = C.node_id AND B.webview_flag = 'Y' AND A.delete_flag = 'N' AND A.qna_id > 'QNAS0000000010' AND ROWNUM = 1 9. 전체게시판 다음글 6.2 EE
  • 202. V. MSSQL 쿼리 VI. 제품 쿼리 살펴보기 VII. 쿼리 작성 팁 VIII. 에필로그 7.1 ROWNUM vs TOP 7.2 OUTER JOIN 7.3 순환관계
  • 203. 7.1 ROWNUM vs TOP ROWNUM이란? 테이블 내에 물리적으로 저장되어 있는 컬럼이 아니라 레코드에 번호를 나타내어 주는 필드이다. ORDER BY, GROUP BY와 같이 사용할 때는 반드시 서브쿼리로 묶어야 정상적인 결과를 가져온다. SELECT * FROM TABLE WHERE ROWNUM < 10 ORDER BY 1 SELECT * FROM ( SELECT * FROM TABLE ORDER BY 1 ) A WHERE ROWNUM < 10
  • 204. MSSQL에서 TOP은 ORACLE의 ROWNUM과 같이 행의 개수를 제한해서 가져온다. SQL7.0에서는 조건에 해당하는 모든 데이터를 읽은 후 FULL SORT 방식으로 결과 추출 SQL2000부터는 ‘Top N Engine’은 N개의 데이터를 임시로 저장할 수 있는 개체(버퍼)를 메모리에 생성 한 후 버퍼에 저장된 가장 크거나 작은 값만을 비교 대상으로 처리한다. SELECT TOP 5 FROM detail ORDER BY quantity DESC 5개의 버퍼를 만들고 테이블 데이터를 순차적으로 읽으면서 이 버퍼를 채운다. 5개의 버퍼가 채워지면 5 번째이후 읽혀진 데이터는 이 버퍼에 저장된 가장 작은 qunatity와 비교된다. 실제 MSSQL에서 TOP.. ORDER BY는 굉장히 빠르다. 그러나 ORACLE의 ROWNUM ORDER BY는 다르다. 실제 쿼리 수행 70만건의 데이터 (0.45초) SELECT TOP 10 * FROM t_bigtable ORDER BY customer_id desc TOP이란? 7.1 ROWNUM vs TOP
  • 205. 7.2 OUTER조인 A B JOIN 2개 이상의 테이블 조인 시 원본 집합은 조인이 발생하 지 않더라도 결과집합에서 나타나도록 하는 것이다. SELECT * FROM tab1, tab2 WHERE tab1.col1 = tab2.col1(+) AND tab1.col2 = ‘A’ SELECT * FROM tab1 LEFT OUTER JOIN tab2 ON tab1.col1 = tab2.col1 WHERE tab1.col2 = ‘A’ SELECT * FROM tab1, tab2 WHERE tab1.col1 *= tab2.col1 AND tab1.col2 = ‘A’ TAB1의 조건을 만족하는 건수가 10 건 TAB1과 TAB2의 조인조건을 만족하 는 건수가 5건 그럼 총 결과는 몇 건이 표시? ANSI ORACLE MSSQL
  • 206. 7.2 OUTER조인 SELECT * FROM t_qna A, t_qna_process B WHERE A.qna_id = B.qna_id(+) AND A.created_date LIKE ‘201012%’ AND B.type IN (‘ANEND’, ‘ANADD’) OUTER 조인을 사용할 때 자주하는 실수입니다. 아래쿼리가 잘못된 이유와 올바른 쿼리로 변경하라. SELECT * FROM t_qna A LEFT OUTER JOIN t_qna_process B ON A.qna_id = B.qna_id AND B.tpye IN (‘ANEND’, ‘ANADD’) WHERE A.created_date LIKE ‘201012%’ SELECT * FROM t_qna A, ( SELECT * FROM t_qna_process B WHERE type IN (‘ANEND’, ‘ANADD’) ) B WHERE A.qna_id = B.qna_id(+) AND A.created_date LIKE ‘201012%’ ANSI ORACLE SELECT * FROM t_qna A, t_qna_process B WHERE A.qna_id = B.qna_id(+) AND A.created_date LIKE ‘201012%’ AND B.type(+) IN (‘ANEND’, ‘ANADD’) Type에 (+)을 넣어주면 될까? IN에는 (+)를 사용할 수 없다.
  • 207. 7.3 순환관계 순환전개란 순환구조를 가진 테이블에서 어떤 점을 시작으로 해서 하위 구조를 전개하는 순전개 (Implosion)이나 역전개(Explosion)을 할때 발생하는 실행계획이다. SELECT LPAD(' ', 2*(LEVEL-1)) || node_name , node_id FROM t_node WHERE delete_flag = 'N' AND depth <= 5 START WITH node_id = 'NODE0000020619' CONNECT BY PRIOR node_id = parent_id FILTER CONNECT BY WITH FILTERING TABLE ACCESS BY INDEX ROWID OF ‘T_NODE’ INDEX UNIQUE SCAN OF ‘PK_NODE’ NESTED LOOPS BUFFER SORT CONNECT BY PUMP TABLE ACCESS BY INDEX ROWID OF ‘T_NODE’ INDEX RANGE SCAN OF ‘IDX_NODE_PARENT_ID’ TABLE ACCESS FULL OF T_NODE 1. START WITH 조건을 만족하는 모든 로우를 엑세스 하여 버퍼에 저장한다. ① ② 3. 오라클 10.2버젼 이상에서 발생하는 특별한 실행계획 trace를 분석해 봐도 실제 전체 테이블 스캔을 하지 않는 것으로 확인됨. ③ 2. PRIOR에 해당하는 값은 버퍼에 저장이 되어있고 parent_id값을 없을때까지 찾아낸다. WHERE 조건은 가장 나중에 단순한 CHECK기능으로만 작용한다. ORACLE
  • 208. 7.3 순환관계 SELECT * FROM t_node WHERE delete_flag = ‘N’ START WITH node_id = ‘NODE0000000001’ CONNECT BY PRIOR node_id = parent_id 순전개 SELECT * FROM t_node WHERE delete_flag = ‘N’ START WITH node_id = ‘NODE0000000001’ CONNECT BY PRIOR parent_id = node_id 역전개 SELECT * FROM t_node WHERE delete_flag = ‘N’ START WITH node_id = ‘NODE0000000001’ CONNECT BY PRIOR node_id = parent_id ORDER SIBLINGS BY sort_order 정렬
  • 209. 5.4.2나 EE에서는 순환관계를 가져올 때 MSSQL의 테이블 리턴 함수를 이용한다. MSSQL2005부터 지원하는 CTE를 이용하면 순환관계 쿼리를 오라클 처럼 사용할 수 있다. CTE (Common Table Expression)는? SELECT쿼리에 기반한 이름이 붙은 임시 결과셋을 지칭한다. SQL2005부터 지원한다. [사용법] WITH CTE이름(column1, column2…) AS ( SELECT 구분 ) SELECT column1, … FROM CTE이름 7.3 순환관계 MSSQL
  • 210. - 스칼라 함수 - 테이블 리턴 함수 스칼라 함수 CREATE FUNCTION 함수명(파라미터) …… RETURN 데이타타입 사용법 : SELECT 사용자명.func(파라미터) FROM TAB1 MSSQL 사용자정의 함수의 종류 테이블 리턴 함수 CREATE FUNCTION 함수명(파라미터) …… RETURN 테이블 사용법 : SELECT * FROM func(파라미터)
  • 211. - EE의 카테고리 계층구조를 CTE를 이용해서 가져오자. WITH nodeCTE (node_id, node_name, parent_id, depth, sort_order, sort_key) AS ( SELECT node_id, node_name, parent_id,depth, sort_order, CAST(sort_order AS VARBINARY(900)) FROM t_node WHERE node_id = 'NODE0000000001' UNION ALL SELECT A.node_id, A.node_name, A.parent_id, A.depth, A.sort_order, CAST( B.sort_key + CAST ( (A.sort_order+1) AS varbinary(900)) AS varbinary(900)) FROM t_node A INNER JOIN nodeCTE B ON A.parent_id = B.node_id ) SELECT REPLICATE(' ', depth) + node_name FROM nodeCTE ORDER BY sort_key 순서정렬을 위한 코드 7.3 순환관계 START WITH에 해당 CONNECT BY PRIOR에 해당
  • 212. 에필로그 ­ 다시 정리해볼까요? 1. 실행계획 2. 인덱스 4. 부분범위처리 • SQL작성 후 실행계획을 확인하라. • SQL 내부 실행과정을 이해하라. • RBO와 CBO의 차이를 이해하라. • Optimzer에 영향을 미치는 요소를 알자. • 실행계획은 읽는 방법을 연습하자. • 인덱스의 기본개념을 이해하자. • 인덱스를 탄다고 무조건 빠른것은 아니다. • 인덱스의 다양한 ACCESS 유형을 알자. • 인덱스를 타지 않는 유형을 알고 제대로 사용 하자. • 결합인덱스의 컬럼순서가 중요한 이유는? 3. 조인 • 조인방법에 따로 처리속도가 다르다. • USE_NL, USE_MERGE, USE_HASH의 기념 개념의 차이를 이해하자. • SEMI조인과 Filter처리와의 차이를 이해하자. • 조인방법 및 성능개선을 위해서 고려해야 할 사항들을 알자. • 전체범위처리와 부분범위처리의 차이를 이해 하자. • 부분범위처리가 적용될 수 있는 원칙은? • 부분범위처리를 통해서 수행속도 향상을 위한 여러가지 방법을 적용하자.