SELECT /*+ INDEX(A IDX_TB_ADDR_01) */ --> WHERE 절 조회 패턴에 상관없이 실행계획 고정
a.*
FROM imsi.tb_addr a
WHERE userid = :b1
AND (name LIKE :b2||'%' OR
email LIKE :b3||'%')
INDEX_NAME | COLUMN LIST |
---|---|
IDX_TB_ADDR_01 | USERID, NAME |
IDX_TB_ADDR_02 | USERID, EMAIL |
IDX_TB_ADDR_03 | UKEY |
PK_TB_ADDR | USERID, UKEY |
-- 테이블 생성
drop table t1;
create table t1 as
select chr(65 + mod(level,4)) as c1
, chr(65 + mod(level,26)) || 'X' as c2
, level || 'X' as c3
from dual
connect by level <= 1000000
;
-- 인덱스 생성
create index t1_idx01 on t1(c1,c2);
create index t1_idx02 on t1(c1,c3);
-- 실행 쿼리
select /*+ index(t1 t1_idx01) */
count(*)
from t1
where c1 = '&b1'
and c2 like '&b2' || '%'
and c3 like '&b3' || '%'
;
-- 변수 설정 (b3 값 null 설정)
define b1 = A
define b2 = AX
define b3 = ''
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 13 | 1347 (1)| 00:00:17 |
| 1 | SORT AGGREGATE | | 1 | 13 | | |
|* 2 | TABLE ACCESS BY INDEX ROWID| T1 | 10245 | 130K| 1347 (1)| 00:00:17 |
|* 3 | INDEX RANGE SCAN | T1_IDX01 | 10245 | | 25 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
-- 효율적으로 T1_IDX01 인덱스 액세스
-- 변수 설정 (b2 값 null 설정)
define b1 = A
define b2 = ''
define b3 = 4X
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 13 | 33315 (1)| 00:06:40 |
| 1 | SORT AGGREGATE | | 1 | 13 | | |
|* 2 | TABLE ACCESS BY INDEX ROWID| T1 | 4 | 52 | 33315 (1)| 00:06:40 |
|* 3 | INDEX RANGE SCAN | T1_IDX01 | 253K| | 574 (2)| 00:00:07 |
-----------------------------------------------------------------------------------------
-- t1_idx02 인덱스를 액세스하는 것이 효율적이지만 실행이 고정되어 있어서 T1_IDX01 인덱스 액세스 함.
-- 개선 후 SQL (SQL을 분리함)
select count(*)
from (select /*+ index(t1 t1_idx01) */
*
from t1
where 1 = &div
and c1 = '&b1'
and c2 like '&b2' || '%'
union all
select /*+ index(t1 t1_idx02) */
*
from t1
where 2 = &div
and c1 = '&b1'
and c3 like '&b3' || '%'
)
;
-- 변수 설정 (b3 값 null 설정)
define div = 1
define b1 = A
define b2 = AX
define b3 = ''
---------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 25 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | | |
| 2 | VIEW | | 10246 | | 25 (0)| 00:00:01 |
| 3 | UNION-ALL | | | | | |
|* 4 | INDEX RANGE SCAN | T1_IDX01 | 10245 | 51225 | 25 (0)| 00:00:01 |
|* 5 | FILTER | | | | | |
|* 6 | INDEX RANGE SCAN| T1_IDX02 | 253K| 2478K| 748 (1)| 00:00:09 |
---------------------------------------------------------------------------------
-- T1_IDX01 인덱스 액세스
-- 변수 설정 (b2 값 null 설정)
define div = 2
define b1 = A
define b2 = ''
define b3 = 4X
---------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 3 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | | |
| 2 | VIEW | | 5 | | 3 (0)| 00:00:01 |
| 3 | UNION-ALL | | | | | |
|* 4 | FILTER | | | | | |
|* 5 | INDEX RANGE SCAN| T1_IDX01 | 253K| 1239K| 574 (2)| 00:00:07 |
|* 6 | INDEX RANGE SCAN | T1_IDX02 | 4 | 40 | 3 (0)| 00:00:01 |
---------------------------------------------------------------------------------
-- T1_IDX02 인덱스 액세스
select *
from cust
where reg_date between :B1 and :B2
;
-- 테이블 생성
drop table t1;
create table t1 as
select to_char(sysdate + mod(level,10000),'YYYYMMDD') c1
, trunc(level/10000) c2
from dual
connect by level <= 1000000
;
-- 인덱스 생성
create index t1_idx01 on t1(c1);
-- 실행 쿼리
select /*+ index(t1 t1_idx01) */
*
from t1
where '&b1' = '&b2'
and c1 between '&b1' and '&b2'
union all
select /*+ full(t1) */
*
from t1
where '&b1' <> '&b2'
and c1 between '&b1' and '&b2'
;
-- 변수 설정 (시작일과 종료일이 동일)
define b1 = '20131201'
define b2 = '20131201'
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 101 | 1919 | 100 (0)| 00:00:02 |
| 1 | UNION-ALL | | | | | |
| 2 | TABLE ACCESS BY INDEX ROWID| T1 | 100 | 1900 | 100 (0)| 00:00:02 |
|* 3 | INDEX RANGE SCAN | T1_IDX01 | 100 | | 3 (0)| 00:00:01 |
|* 4 | FILTER | | | | | |
|* 5 | TABLE ACCESS FULL | T1 | 100 | 1900 | 667 (3)| 00:00:09 |
-----------------------------------------------------------------------------------------
-- 시작일과 종료일이 동일하므로 상단 쿼리만 실행되어서 인덱스 액세스 함.
-- 변수 설정 (1년 조회)
define b1 = '20131201'
define b2 = '20141131'
------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 46248 | 858K| 667 (100)| 00:00:09 |
| 1 | UNION-ALL | | | | | |
|* 2 | FILTER | | | | | |
| 3 | TABLE ACCESS BY INDEX ROWID| T1 | 46247 | 858K| 44734 (1)| 00:08:57 |
|* 4 | INDEX RANGE SCAN | T1_IDX01 | 46247 | | 130 (1)| 00:00:02 |
|* 5 | TABLE ACCESS FULL | T1 | 46247 | 858K| 667 (3)| 00:00:09 |
------------------------------------------------------------------------------------------
-- 시작일과 종료일이 다르므로 하단 쿼리만 실행되어서 풀테이블 스캔 함.
SELECT name,
TO_CHAR(last_captured,'yyyymmdd hh24:mi:ss') last_captured,
datatype,
value_string
FROM DBA_HIST_SQLBIND
WHERE sql_id = :sql_id ;
NAME LAST_CAPTURED DATATYPE VALUE_STRING
---- ----------------- ------- -----------
:B65 20101213 14:46:06 1 201011
:B66 20101213 14:46:06 1 0403
:B67 20101213 14:46:06 1 00000000000 ---> SN_NO 조회 시작 값
:B68 20101213 14:46:06 1 99999999999 ---> SN_NO 조회 끝 값
:B65 20101227 16:06:59 1 201012
:B66 20101227 16:06:59 1 0331
:B67 20101227 16:06:59 1 57011155797 ---> SN_NO 조회 시작 값
:B68 20101227 16:06:59 1 57011155797 ---> SN_NO 조회 끝 값
-- 크게 두 가지 패턴으로 나뉜다는 것을 알 수 있음.
-- 두 가지 패턴에 따른 SQL 분리를 하면 됨.
- 강좌 URL : http://www.gurubee.net/lecture/3822
- 구루비 강좌는 개인의 학습용으로만 사용 할 수 있으며, 다른 웹 페이지에 게재할 경우에는 출처를 꼭 밝혀 주시면 고맙겠습니다.~^^
- 구루비 강좌는 서비스 제공을 위한 목적이나, 학원 홍보, 수익을 얻기 위한 용도로 사용 할 수 없습니다.