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
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) 실행계획의 비용 계산 결과
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 인덱스의 활용성 방안
- 인덱스의 활용(적용기준)
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))
으로 사용
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)에 해당 되는 데이터를 뽑아내는 것을 말한다.
결과는?
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
- 엑세스 경로의 결정
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
- 특징
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 조인방법 결정 및 성능 개선 팩터
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
결과는 홍길동이 나온다.
그룹함수는 최소 한건의 데이터는 나온다.
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 인덱스
복합인덱스는 둘 이상의 컬럼으로 만드는 인덱스이다.
복합인덱스
쿼리 검색에 필요한 모든 컬럼들이 포함된(넌클러스터) 인덱스를 말한다.
인덱스 검색 후 북마크 룩업을 해야 하는데 필요한 모든 컬럼 값이 인덱스에 이미 다
있으므로 북마크 룩업 과정은 생략된다.
커버된 인덱스
둘 이상의 인덱스를 동시에 사용하여 인덱스 교집합을 만든다.
인덱스 교집합
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만건 이라면 어떤 인덱스를
추가해야 하고 쿼리를 어떻게 변경을 해야
할까?
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처리와의 차이를 이해하자.
• 조인방법 및 성능개선을 위해서 고려해야 할
사항들을 알자.
• 전체범위처리와 부분범위처리의 차이를 이해
하자.
• 부분범위처리가 적용될 수 있는 원칙은?
• 부분범위처리를 통해서 수행속도 향상을 위한
여러가지 방법을 적용하자.