(오라클,sql힌트튜닝 무료양방향화상교육#1)인덱스스캐닝 관련힌트,_index,...

18
오라클 힌트/튜닝 온라인 화상(양방향) 교육 1일차 - INDEX, INDEX_ASC, INDEX_DESC, INDEX_RS, NO_INDEX - INDEX_FFS, INDEX_SS, - USE_INVISIBLE_INDEXES, NO_USE_INVISIBLE_INDEX

Upload: 3-2

Post on 07-Jan-2017

191 views

Category:

Education


1 download

TRANSCRIPT

Page 1: (오라클,SQL힌트튜닝 무료양방향화상교육#1)인덱스스캐닝 관련힌트,_INDEX, INDEX_ASC, INDEX_DESC, INDEX_RS, NO_INDEX, INDEX_FFS, INDEX_SS, USE_INVISIBLE_INDEXES,

오라클 힌트/튜닝

온라인 화상(양방향) 교육 1일차

- INDEX, INDEX_ASC, INDEX_DESC, INDEX_RS, NO_INDEX

- INDEX_FFS, INDEX_SS,

- USE_INVISIBLE_INDEXES, NO_USE_INVISIBLE_INDEX

Page 2: (오라클,SQL힌트튜닝 무료양방향화상교육#1)인덱스스캐닝 관련힌트,_INDEX, INDEX_ASC, INDEX_DESC, INDEX_RS, NO_INDEX, INDEX_FFS, INDEX_SS, USE_INVISIBLE_INDEXES,

1.Hints For Access Paths(INDEX, INDEX_COMBINE)

INDEX 힌트는 테이블의 칼럼에 대해 생성되어 있는 인덱스를 사용할 수 있도록 해주는 힌트

구문으로 비트맵 인덱스에 대해서도 사용이 가능하지만 Bitmap Index는 INDEX_COMBINE

사용하는 것이 원칙이다.

인덱스 영역에서 인덱스가 생성된 형태 대로 순방향 스캐닝 하므로 INDEX_ASC와 동일하다.

대량의 데이터라면 ORDER BY의 사용을 자제하고 인덱스를 적젃히 이용하자.

[형식]

/*+ INDEX(테이블명 [인덱스명 [인덱스명] … ]) */

2.Hints For Access Paths(INDEX_ASC)

INDEX 힌트와 동일한데 인덱스가 생성된 형태대로 인덱스를 스캔 하라는 의미의 힌트이다.

이 힌트를 이용하여 데이터를 추출하게 되면 화면에 나타나는 데이터는 인덱스를 생성한

순서대로 데이터가 추출된다.

인덱스 영역에서 인덱스가 생성된 형태 대로 순방향 스캐닝 하므로 INDEX 힌트와 동일하다.

[형식]

/*+ INDEX_ASC(테이블명 [인덱스명 [인덱스명] … ]) */

Page 3: (오라클,SQL힌트튜닝 무료양방향화상교육#1)인덱스스캐닝 관련힌트,_INDEX, INDEX_ASC, INDEX_DESC, INDEX_RS, NO_INDEX, INDEX_FFS, INDEX_SS, USE_INVISIBLE_INDEXES,

3.Hints For Access Paths(INDEX_DESC)

INDEX, INDEX_ASC 힌트의 반대로 인덱스 영역에서 생성된 인덱스의 역순으로 스캐닝

하라는 의미로 데이터 값을 역순 정렬하라는 의미는 아니다. 인덱스가 생성될 때

내림차순으로 생성 되었다면 이 힌트를 사용한다면 데이터 값은 오름차순으로 나타나게 된다.

[형식]

/*+ INDEX_DESC(테이블명 [인덱스명 [인덱스명] … ]) */

아래 예문은 ORDER BY, 인덱스, 인덱스와 관렦된 힌트를 이용한 예제이다.

-- MYEMP1 테이블은 1000맊건 정도의 데이터가 있으며 ENAME 칼럼에 인덱스가 생성 되어 있

다. (idx_myemp1_ename, 내림차순으로 생성)

SQL> DROP INDEX IDX_MYEMP1_ENAME

SQL> CREATE INDEX IDX_MYEMP1_ENAME ON MYEMP1(ename desc)

ENAME 내림차순이므로 하길동... 김길동 순으로 인덱스에 정렧되어 있고, 인덱스에서 순방향으로

스캔(INDEX, INDEX_ASC)해서 데이터를 뿌리면 내림차순 정렧된다. 하지맊 INDEX_DESC와 같은

힌트를 사용하면 오름차순으로 정렧되어 나타날 것이다. 즉 INDEX_ASC 힌트는 데이터 오름차순

으로 정렧한다는 것이 아니라 인덱스 영역에서 데이터를 순방향 스캔 한다는 의미이다.

-- ename칼럼에 생성된 인덱스를 경유하므로 데이터가 이름 역순으로 출력

SQL> SELECT ename FROM myemp1;

-- ename이외의 다른 칼럼을 같이 SELECT 하는 경우엔 원본 테이블 FULL SCAN

SQL> SELECT ename, sal FROM myemp1;

-- ename 이외의 다른 칼럼을 같이 SELECT 하는 경우 정렧을 원하면 WHERE절에

-- ENAME을 출현시킴

SELECT ename, sal FROM myemp1 where ename > 'ㄱ';

-- WHERE절에 ENAME 사용되어서 인덱스 영역에서 스캐닝, order by 때문에 SELECT되는 레코

드가 이름 오름차순..., 0초

SQL> SELECT ENAME FROM MYEMP1 WHERE ENAME >= 'ㄱ' ORDER BY ENAME

-- WHERE절에 ENAME 사용되어서 인덱스 영역에서 스캐닝, 원래 인덱스가 내림차순으로 생성

되어서 이름 내림차순으로 display

Page 4: (오라클,SQL힌트튜닝 무료양방향화상교육#1)인덱스스캐닝 관련힌트,_INDEX, INDEX_ASC, INDEX_DESC, INDEX_RS, NO_INDEX, INDEX_FFS, INDEX_SS, USE_INVISIBLE_INDEXES,

SQL> SELECT ENAME FROM MYEMP1 WHERE ENAME >= 'ㄱ'

-- SELECT절에 나타나는 칼럼이 인덱스 칼럼이 아니더라도... WHERE절에 ENAME이 출현하므로

인덱스 영역에서 스캔, 이름 내림차순으로

SQL> SELECT EMPNO, ENAME, SAL FROM MYEMP1 WHERE ENAME >= '가'

-- 현재 ename 인덱스는 내림차순으로 생성되어 있다. 인덱스 영역에서 순방향으로 스캔한다면

이름, 내림차순으로 출력 될 것이다.

-- index_asc, index 힌트 : 인덱스 영역에서 순방향으로 스캔 하라는 뜻

SQL> SELECT /*+ index_asc(e idx_myemp1_ename) */

EMPNO, ENAME, SAL FROM MYEMP1 e

WHERE ENAME >= '가'

-- index_desc 힌트 : 인덱스 영역에서 역방향으로 스캔 하라는 뜻

SQL> SELECT /*+ index_desc(e idx_myemp1_ename) */

EMPNO, ENAME, SAL FROM MYEMP1 E

WHERE ENAME >= '가'

이번에는 순위와 관렦된 질의를 해보자.

MYEMP1 Table에서 급여(SAL)가 맋은 순서로 1위부터 5위까지 Fetch

(현재 SAL 칼럼에는 오름차순 인덱스가 생성되어 있다.)

SQL> SELECT a.index_name

FROM USER_IND_COLUMNS a, USER_INDEXES b

WHERE a.table_name = 'MYEMP1'

AND a.index_name = b.index_name

AND a.column_name = 'SAL'

ORDER BY a.index_name, a.column_name;

INDEX_NAME

------------------------------

IDX_MYEMP1_SAL

SQL> SELECT ename, sal

FROM (

SELECT ename, sal

FROM myemp1

ORDER BY sal DESC

)

Page 5: (오라클,SQL힌트튜닝 무료양방향화상교육#1)인덱스스캐닝 관련힌트,_INDEX, INDEX_ASC, INDEX_DESC, INDEX_RS, NO_INDEX, INDEX_FFS, INDEX_SS, USE_INVISIBLE_INDEXES,

WHERE rownum >= 1

AND rownum <= 5

ENAME SAL

-----------------------

가길동 5999999

나길동 5999997

다길동 5999996

마길동 5999998

홍길동 5999995

느리다.

Execution Plan

-----------------------------------------------------------------------

| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CP

-----------------------------------------------------------------------

| 0 | SELECT STATEMENT | | 1 | 21 | | 8509M

| 1 | SORT ORDER BY | | 1 | 21 | 612M| 8509M

|* 2 | FILTER | | | | |

| 3 | TABLE ACCESS FULL| MYEMP1 | 20M| 400M| | 43771

| 4 | SORT AGGREGATE | | 1 | 6 | |

|* 5 | INDEX RANGE SCAN| IDX_MYEMP1_SAL | 1000K| 5859K| |

-----------------------------------------------------------------------

-- 위 쿼리와 실행계획 같다. 마찬가지로 데이터 추출시까지 한참 기다려야 한다.

SQL> select ename, sal from myemp1 a

where 5 > (select count(*)

from myemp1 b

where b.sal > a.sal)

order by sal desc;

-- 이번에는 인덱스 관렦 힌트를 이용하여 인덱스를 적절히 이용해 보자.

SQL> SELECT ename, sal

FROM (

SELECT /*+ index_desc(myemp1 idx_myemp1_sal) */

ename, sal

FROM myemp1

WHERE sal > 0

Page 6: (오라클,SQL힌트튜닝 무료양방향화상교육#1)인덱스스캐닝 관련힌트,_INDEX, INDEX_ASC, INDEX_DESC, INDEX_RS, NO_INDEX, INDEX_FFS, INDEX_SS, USE_INVISIBLE_INDEXES,

AND rownum <= 5

)

WHERE rownum >= 1

ORDER BY ename

ENAME SAL

-----------------------

가길동 5999999

나길동 5999997

다길동 5999996

마길동 5999998

홍길동 5999995

Execution Plan

-----------------------------------------------------------------------

| Id | Operation | Name | Rows | Bytes | Cost (%

-----------------------------------------------------------------------

| 0 | SELECT STATEMENT | | 4 | 84 | 7

|* 1 | COUNT STOPKEY | | | |

| 2 | TABLE ACCESS BY INDEX ROWID | MYEMP1 | 4 | 84 |

|* 3 | INDEX RANGE SCAN DESCENDING| IDX_MYEMP1_SAL | 19M| |

-----------------------------------------------------------------------

-- 분포도가 좋지않은 컬럼(B*Tree인덱스)의 count연산시 index fast full scan과 index range

scan의 성능 차이 비교

비트리 인덱스인 경우 분포도가 좋지 않은 컬럼을 count하는 경우라면 index fast full scan보다

index range scan이 좋을 것 같다.

myemp1 테이블의 성별(sungbyul) 칼럼은 'M' or 'F' 값을 가지며 값의 분포도는 50% 이다.

SQL> desc myemp1

이름 널 유형

-------- -------- -------------

EMPNO NOT NULL NUMBER

ENAME VARCHAR2(100)

DEPTNO VARCHAR2(1)

ADDR VARCHAR2(100)

SAL NUMBER

Page 7: (오라클,SQL힌트튜닝 무료양방향화상교육#1)인덱스스캐닝 관련힌트,_INDEX, INDEX_ASC, INDEX_DESC, INDEX_RS, NO_INDEX, INDEX_FFS, INDEX_SS, USE_INVISIBLE_INDEXES,

SUNGBYUL VARCHAR2(1)

-- 비트리 인덱스를 맊들자.

SQL> create index idx_myemp1_sungbyul on myemp1(SUNGBYUL)

1. CBO MODE에서 COUNT를 하면 기본적으로 index fast full 스캔을 한다.

(수행시간 : 0.8, 0.38초)

SQL> alter session set optimizer_mode = all_rows;

SQL> show parameter optimizer_mode

SQL> select count(*) from myemp1 where sungbyul = 'M';

COUNT(*)

----------

12000000

경 과: 00:00:00.08

Execution Plan

----------------------------------------------------------------------------------------------------------

| Id | Operation | Name | Rows | Bytes | Cost (%CPU

----------------------------------------------------------------------------------------------------------

| 0 | SELECT STATEMENT | | 1 | 2 | 9995 (2

| 1 | S O R T A G G R E G AT E | | 1 | 2 |

|* 2 | INDEX FAST FULL SCAN| IDX_MYEMP1_SUNGBYUL | 10M| 19M| 9995 (2

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

3. index_rs or index 힌트를 사용해도 된다.

SQL> select /*+ index_rs(myemp1 idx_myemp1_sungbyul) */ count(*) from myemp1 where

sungbyul = 'M';

SQL> select /*+ index(myemp1 idx_myemp1_sungbyul) */ count(*) from myemp1 where

sungbyul = 'M';

COUNT(*)

----------

10000002

Page 8: (오라클,SQL힌트튜닝 무료양방향화상교육#1)인덱스스캐닝 관련힌트,_INDEX, INDEX_ASC, INDEX_DESC, INDEX_RS, NO_INDEX, INDEX_FFS, INDEX_SS, USE_INVISIBLE_INDEXES,

경 과: 00:00:00.17

Execution Plan

----------------------------------------------------------

| 0 | SELECT STATEMENT | | 1 | 1 | 18223 (1)| 0

| 1 | SORT AGGREGATE | | 1 | 1 | |

|* 2 | INDEX RANGE SCAN| IDX_MYEMP1_SUNGBYUL | 10M| 9765K| 18223 (1)| 0

4.Hints For Access Paths(NO_INDEX)

인자로 주어진 인덱스를 사용하지 말라는 의미인데 인덱스를 인자로 안주고 테이블만 인자로

준다면 그 테이블에서 생성된 어떠한 인덱스도 사용하지 말라는 뜻이다.

[형식]

/*+ NO_INDEX(테이블명 [인덱스명 [인덱스명] … ]) */

[인덱스를 사용하는 경우]

SQL> SELECT /*+ INDEX(EMP idx_emp_ename) */

ename, sal

FROM EMP

WHERE ENAME LIKE 'S%';

----------------------------------------------------------------

Operation Object Name Rows Bytes Cost

----------------------------------------------------------------

SELECT STATEMENT Optimizer Mode=ALL_ROWS 2 2

TABLE ACCESS BY INDEX ROWID EMP 2 18 2

INDEX RANGE SCAN IDX_EMP_ENAME 2 1

[NO_INDEX를 사용하는 경우]

SQL> SELECT /*+ NO_INDEX(EMP idx_emp_ename) */

ename, sal

FROM EMP

WHERE ENAME LIKE 'S%';

----------------------------------------------------------------

Operation Object Name Rows Bytes Cost

Page 9: (오라클,SQL힌트튜닝 무료양방향화상교육#1)인덱스스캐닝 관련힌트,_INDEX, INDEX_ASC, INDEX_DESC, INDEX_RS, NO_INDEX, INDEX_FFS, INDEX_SS, USE_INVISIBLE_INDEXES,

----------------------------------------------------------------

SELECT STATEMENT Optimizer Mode=ALL_ROWS 2 3

TABLE ACCESS FULL EMP 2 18 3

5.use_invisible_indexes, no_use_invisible_indexes 힌트

Oracle11g R1 부터 Optimizer에게 보이지 않는 invisible index의 생성을 가능하도록 했는데

인덱스는 Index Segment에 동일하게 생성되지만 단지 보이는 않는다.

실무에서 인덱스 변경을 통해 SQL 성능을 평가하는 경우가 많이 있으므로 실제 인덱스를 반

영하기 젂에 그 인덱스가 사용되었을 때의 성능을 평가하기 위해 사용한다.

Invisible Index의 생성 방법은 다음과 같다.

CREATE INDEX 인덱스이름 ON 테이블이름(컬럼명,,,) INVISIBLE;

ALTER INDEX 인덱스이름 INVISIBLE; -- 인덱스 안보이도록

ALTER INDEX 인덱스이름 VISIBLE; -- 인덱스 보이도록

SQL> create index idx_myemp1_ename on myemp1(ename) invisible;

SQL> set autotrace on explain

SQL> set timing on

-- 인덱스가 invisible 상태 이므로 옵티마이저가 만든 실행계획에 반영되지 않는다.

SQL> select count(1) from myemp1

where ename like '가%';

-- use_invisible_indexes 힌트를 사용하면 옵티마이저가 인덱스를 사용한다.

SQL> select /*+ use_invisible_indexes */ count(1)

from myemp1 where ename like '가%';

COUNT(1)

----------

Page 10: (오라클,SQL힌트튜닝 무료양방향화상교육#1)인덱스스캐닝 관련힌트,_INDEX, INDEX_ASC, INDEX_DESC, INDEX_RS, NO_INDEX, INDEX_FFS, INDEX_SS, USE_INVISIBLE_INDEXES,

1666666

-- 아래와 같이 세션 레벨에서 invisible index의 사용을 허가해도 된다.

SQL> alter session set optimizer_use_invisible_indexes = true;

SQL> select count(1) from myemp1

where ename like '가%';

COUNT(1)

----------

1666666

-- 아래는 invisible index의 사용을 막았다.

SQL> select /*+ no_use_invisible_indexes */ count(1) from myemp1 where ename like '가%';

COUNT(1)

----------

1666666

경 과: 00:00:01.54

Page 11: (오라클,SQL힌트튜닝 무료양방향화상교육#1)인덱스스캐닝 관련힌트,_INDEX, INDEX_ASC, INDEX_DESC, INDEX_RS, NO_INDEX, INDEX_FFS, INDEX_SS, USE_INVISIBLE_INDEXES,

6.Hints For Access Paths(INDEX_FFS)

인덱스를 FAST FULL SCAN 하라는 것인데, 보통 인덱스에 대한 스캔은 단일 블록 스캔인데

반해 인덱스 패스트 풀 스캔은 Multi Block Scan 이다. 데이터를 읽을 때 하나씩 블록을 읽는

것보다 여러 개의 블록을 읽는다면 당연히 수행속도가 빨라질 것이다.

이 힌트가 동작하기 위해서는 SELECT젃에 나타나는 컬럼 들이 INDEX_FFS 의 인자로 사용된

인덱스의 컬럼 이어야 한다. 예를 들어 index_ffs 의 인자로 idx_emp_ename(EMP 테이블의

ename 컬럼에 대한 인덱스) 이 주어졌다면 SELECT 젃에 나열되는 컬럼이 ename 이여야

한다는 것이다.

[형식]

/*+ INDEX_FFS ( table [index [index]...] ) */

minus로 빼는 연산인 경우 select 리스트의 컬럼에 대해 인덱스를 맊들고 index_ffs 힌트를 이용

해 인덱스 패스트 풀 스캔하면 성능면에서 장점이 있다. MYEMP1, MYEMP1_OLD 테이블의

ENAME 칼럼에 인덱스를 확인하고 없다면 아래 스크립트를 실행하자.

SQL> create index idx_myemp1_ename on myemp1(ename);

SQL> create index idx_myemp1_old_ename on myemp1_old(ename);

-- ENAME 칼럼에 인덱스가 있더라도 사용하지 못하고 두 테이블을 FULL SCAN 한다.

SQL> WITH a AS (

SELECT ename FROM myemp1

MINUS

SELECT ename FROM myemp1_old

)

SELECT count(ename) FROM a;

COUNT(ENAME)

------------

8333335

경 과: 00:00:40.83

Execution Plan

- - -- -- - -- -- - -- -- -- - -- -- - -- - -- -- - -- -- - -- -- -- - -- -- - -- - -- -- - -- -- - -- -- -- - -- -- - -- - -- -- - -- -- - -----

| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |

Page 12: (오라클,SQL힌트튜닝 무료양방향화상교육#1)인덱스스캐닝 관련힌트,_INDEX, INDEX_ASC, INDEX_DESC, INDEX_RS, NO_INDEX, INDEX_FFS, INDEX_SS, USE_INVISIBLE_INDEXES,

- - -- -- - -- -- - -- -- -- - -- -- - -- - -- -- - -- -- - -- -- -- - -- -- - -- - -- -- - -- -- - -- -- -- - - - -- - -- - -- -- - -- -- - -----

| 0 | SELECT STATEMENT | | 1 | 52 | | 84936 (2)| 00:17:00 |

| 1 | SORT AGGREGATE | | 1 | 52 | | | |

| 2 | VIEW | | 10M| 495M| | 84936 (2)| 00:17:00 |

| 3 | MINUS | | | | | | |

| 4 | SORT UNIQUE | | 10M| 133M| 191M| 75043 (2)| 00:15:01 |

| 5 | TABLE ACCESS FULL| MYEMP1 | 10M| 133M| | 25173 (1)| 00:05:03 |

| 6 | SORT UNIQUE | | 1666K| 22M| 31M| 9893 (2)| 00:01:59 |

| 7 | TABLE ACCESS FULL| MYEMP1_OLD | 1666K| 22M| | 1592 (2)| 00:00:20 |

- - - - -- - -- -- - -- -- -- - -- -- - -- - -- -- - -- -- - -- -- -- - -- -- - -- - -- -- - -- -- - -- -- -- - -- -- - -- - -- -- - -- -- - -----

-- 이번에는 INDEX_FFS 힌트를 사용하여 INDEX FAST FULL SCAN이 일어나도록 하자. WHERE

절에 ENAME 칼럼을 출현시켜야 힌트가 적용된다. 물롞 힌트를 적용하지 않고 WHERE절에맊

ENAME 칼럼을 출현시켜도 CBO에서 INDEX FAST FULL SCAN 하니 확인하기 바란다.

SQL> WITH a AS (

SELECT /*+ index_ffs(myemp1 idx_myemp1_ename) */ ename FROM myemp1

WHERE ename IS NOT NULL

MINUS

SELECT /*+index_ffs(myemp1_oldidx_myemp1_old_ename) */ ename FROM

myemp1_old

WHERE ename IS NOT NULL

)

SELECT count(ename) FROM a ;

COUNT(ENAME)

------------

8333335

경 과: 00:00:1.79

Execution Plan

-- -- -- - -- -- - -- -- -- - -- -- - -- -- -- -- -- - -- -- - -- -- -- - -- -- - -- -- -- -- -- - -- -- - -- -- -- - -- -- - -- -- -- -- -- - -- -- - -- -- -- - --

| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |

-- -- -- - -- -- - -- -- -- - -- -- - -- -- -- -- -- - -- -- - -- -- -- - -- -- - -- -- -- -- -- - -- -- - -- -- -- - -- -- - -- -- -- -- -- - -- -- - -- -- -- - --

| 0 | SELECT STATEMENT | | 1 | 52 | | 69169 (2)| 00:13:51 |

| 1 | SORT AGGREGATE | | 1 | 52 | | | |

| 2 | VIEW | | 10M| 495M| | 69169 (2)| 0 0:13:51 |

| 3 | MINUS | | | | | | |

| 4 | SORT UNIQUE | | 10M| 133M| 191M| 59284 (2)| 00:11:52 |

|* 5 | INDEX FAST FULL SCAN| IDX_MYEMP1_ENAME | 10M| 133M| | 9415 (2)| 00:01:53 |

| 6 | SORT UNIQUE | | 1666K| 22M| 31M| 9885 (2)| 00:01:59 |

|* 7 | INDEX FAST FULL SCAN| IDX_MYEMP1_OLD_ENAME | 1666K| 22M| | 1583 (2)| 00:00:19 |

-- -- -- - -- -- - -- -- -- - -- -- - -- -- -- -- -- - -- -- - -- -- -- - -- -- - -- -- -- -- -- - -- -- - -- -- -- - -- -- - -- -- -- -- -- - -- -- - -- -- -- - --

Page 13: (오라클,SQL힌트튜닝 무료양방향화상교육#1)인덱스스캐닝 관련힌트,_INDEX, INDEX_ASC, INDEX_DESC, INDEX_RS, NO_INDEX, INDEX_FFS, INDEX_SS, USE_INVISIBLE_INDEXES,

7.Hints For Access Paths(INDEX_SS)

“복합(결합) 인덱스(composite index)”의 경우 가장 많이 사용되고 분포도가 좋은 컬럼을

leading column으로 하는 것을 원칙으로 하고 있는데 그 이유는 where젃에 leading

column 은 기술하지 않고 그 외의 컬럼을 기술하는 경우에는 index를 사용 할 수 없지만

index 구성 젂체 칼럼을 where젃에 기술하는 경우에는 인덱스의 사용이 가능 했기 때문이다.

Oracle9i 이젂까지 결합인덱스인 경우 선두 칼럼이 WHERE젃에 출현하지 않으면 인덱스를

이용하지 못했지만 9i이후 부터는 INDEX SKIP SCAN 이라는 연산을 이용하여 인덱스가

사용가능 하도록 했다.

ROOT, BRANCH BLOCK 에서 읽은 선두 칼럼을 스킵하면서 후행 칼럼 값이 있을만한 LEAF

BLOCK을 검색하는 것을 INDEX SKIP SCAN 이라고 한다.

SELECT젃에서 추출되는 칼럼 항목이 index 영역에서 젂부 가지고 올 수 있는 경우엔

CBO인 경우 선두칼럼이 WHERE 젃에 없더라도 인덱스를 FULL SCAN하여 데이터를 가지고

온다.

reverse key, bitmap, function-based index는 지원하지 않는다.

SKIP SCANNING에서 선두 칼럼의 DISTINCT VALUE 가 적도록 하고 NON-LEADING

COLUMN의 DISTINCT VALUE가 클수록 유리한 스캔방법이다.

하지만 대체적으로 WHERE젃에 인덱스 선행 칼럼을 명시하는 것이 좋다.복합 인덱스의 경우,

선행 인덱스가 WHERE젃에 명시되어 있다면 쿼리는 그 인덱스 를 사용할 것이다.

INDEX_SS : 인덱스 Skip Scan 해달라는 힌트

NO_INDEX_SS : 인덱스 Skip Scan 하지 말라는 힌트

INDEX_SS_ASC : 인덱스영역에서 오름차순(순방향 스캔) Skip Scan 해달라는 힌트

INDEX_SS_DESC : 인덱스영역에서 내림름차순(역방향 스캔) Skip Scan 해달라는 힌트

[형식]

/*+ INDEX_SS ( table [index [index]...] ) */

-- 실습홖경 : 오라클11g R2, CBO, Optimizer Mode : ALL_ROWS

-- 이미 생성된 ename 칼럼 인덱스 및 복합인덱스를 삭제

1000맊건인 myemp1에서 부서코드(deptno) 컬럼이 가지는 값은 ‘0’,’1’,’2’,’3’,’4’ 이니 이 컬럼을

선두 컬럼으로하고 ename 컬럼을 다음에 그리고 sal 컬럼을 위치 시키자.

drop index idx_myemp1_ename;

drop index idx_myemp1_deptno_ename_sal;

create index idx_myemp1_deptno_ename_sal on myemp1(deptno, ename, sal) nologging;

Page 14: (오라클,SQL힌트튜닝 무료양방향화상교육#1)인덱스스캐닝 관련힌트,_INDEX, INDEX_ASC, INDEX_DESC, INDEX_RS, NO_INDEX, INDEX_FFS, INDEX_SS, USE_INVISIBLE_INDEXES,

결과를 보면 알겠지만 리딩컬럼을 제외하고 쿼리 했을 때 대체적으로 ISS(Index Skip Scanning)

을 이용했으면 큰 무리는 없었다.

-- 선두칼럼을 WHERE 절에 출현시키고 힌트없이 실행

-- INDEX RANGE SCAN을 한다. 0.6초

select count(ename) from myemp1 where deptno = '1';

-- INDEX FAST FULL SCAN을 하도록 힌트를 사용해 보자. 1.9초

-- INDEX_FFS의 동작을 위해서는 SELECT 칼럼이 인덱스 구성칼럼맊으로 되야한다

select /*+ index_ffs(myemp1 idx_myemp1_deptno_ename_sal) */

count(ename)

from myemp1 where deptno = '1';

-- WHERE절에 선두칼럼 없이 후행칼럼이 출현, INDEX SKIP SCAN 한다. 0초

select count(ename) from myemp1 where ename = '홍길동4567';

-- INDEX SKIP SCAN하지 말라는 힌트 사용

-- INDEX FAST FULL SCAN 한다. 0.4초

select /*+ no_index_ss(myemp1 idx_myemp1_deptno_ename_sal) */

count(ename)

from myemp1 where ename = '홍길동4567';

Page 15: (오라클,SQL힌트튜닝 무료양방향화상교육#1)인덱스스캐닝 관련힌트,_INDEX, INDEX_ASC, INDEX_DESC, INDEX_RS, NO_INDEX, INDEX_FFS, INDEX_SS, USE_INVISIBLE_INDEXES,

-- 힌트없이 후행칼럼 ename, sal를 모두 WHERE절에 출현하도록

-- INDEX SKIP SCAN이 효율적인데 오라클은 INDEX FAST FULL SCAN한다. 1.5초

select count(ename) from myemp1

where ename > '홍길동456771'

and sal > 1999999;

-- INDEX SKIP SCAN 하도록 힌트사용 0.2초

select /*+ index_ss(myemp1 idx_myemp1_deptno_ename_sal) */

count(ename) from myemp1

where ename > '홍길동456771'

and sal > 1999999;

-- INDEX SKIP SCAN DESCENDING 연산을 한다.

-- 인덱스 영역의 뒷부분 부터 데이터 스캐닝

-- 인덱스 생성이 오름차순이므로 내림차순출력

select /*+ index_ss_desc(myemp1 idx_myemp1_deptno_ename_sal) */

deptno, ename, sal from myemp1

where ename > '홍길동456771'

and sal > 1999999;

Page 16: (오라클,SQL힌트튜닝 무료양방향화상교육#1)인덱스스캐닝 관련힌트,_INDEX, INDEX_ASC, INDEX_DESC, INDEX_RS, NO_INDEX, INDEX_FFS, INDEX_SS, USE_INVISIBLE_INDEXES,

-- index_ss_asc, index_ss는 동일한 동작을 한다.

-- INDEX SKIP SCAN 연산을 한다.(기본이 인덱스영역 순방향 스캔)

-- 인덱스 영역의 앞부분 부터 데이터 스캐닝

-- 인덱스 생성이 오름차순이므로 오름차순출력

select /*+ index_ss_asc(myemp1 idx_myemp1_deptno_ename_sal) */

deptno, ename, sal from myemp1

where ename > '홍길동456771'

and sal > 1999999;

-- 항상 참인 조건을 기술하여 선두 칼럼을 출현시켜 보자.

-- INDEX FAST FULL SCAN 한다. 1.4초

select count(ename) from myemp1

where deptno >= '0'

and ename > '홍길동456771'

and sal > 1999999;

-- 무조건 참인 조건을 기술하여 선두 칼럼을 출현시켜 보자.

-- INDEX_RS힌트로 INDEX RANGE SCAN을 유도해 보자. 0.6초

select /*+ index_rs(myemp1 idx_myemp1_deptno_ename_sal) */

count(ename) from myemp1

where deptno >= '0'

and ename > '홍길동456771'

and sal > 1999999;

Page 17: (오라클,SQL힌트튜닝 무료양방향화상교육#1)인덱스스캐닝 관련힌트,_INDEX, INDEX_ASC, INDEX_DESC, INDEX_RS, NO_INDEX, INDEX_FFS, INDEX_SS, USE_INVISIBLE_INDEXES,