본문 바로가기

sql_tuning

2. 인덱스 튜닝

728x90
반응형

인덱스 튜닝

*데이터를 엑세스 하는 방법 2가지

 

1. full table scan : /*+ full(테이블명) */

 

2. index scan

   -index range scan : /*+ index(테이블명 인덱스명) */

   -index unique scan : /*+ index(테이블명 인덱스명) */

   -index full scan:      /*+ index_fs(테이블명 인덱스명) */

   -index fast full scan: /*+ index_ffs(테이블명 인덱스명) */

   -index skip scan: /*+ index_ss(테이블명 인덱스명) */

   -index merge scan: /*+ and_equal(테이블명 인덱스명) */

   -index bitmap merge scan: /*+ index_combine(테이블명 인덱스명) */

 

 

table full scan(=full table scan)

 테이블 전체를 스캔하는 데이터 엑세스 방법

 

*full table scan 인덱스 스캔보다 유리한 경우?

  테이블에서 검색하려는 데이터의 양이 많을

   (인덱스를 통해서 데이터를 검색하는 양이 많아서

       오히려 느려질 )

 

*full table scan 밖에 없는 경우

1. 병렬처리

 : select /*+ parallel(e 4) */ empno, ename, sal

        from emp e

 

 emp테이블을 4개의 프로세서가 나눠서 읽는다.      

 (일의 분담화)

 

2. 인덱스 생성할

: create index emp_sal

       on emp(sal);

 

 

 

 

3. 인덱스를 재구성

 : alter index emp_sal rebuild ;

필요하냐면

본문내용 절반이 지워졌으면

인덱스도 해당내용을 까맣게 지워놓는다.

(없어지진 않는다. 죽은이파리 처럼 남아있는다.)

죽은이파리를 없애고 깔끔하게 정렬하기 위해

위의 재구성을 돌리고

full scan 한다.

 

 

인덱스 재구성 테스트

 "테이블의 데이터를 수정하게 되면 테이블의 데이터는

변경되지만 인덱스는 변경하지 않고 그냥 남아있게 된다."

 

 

나무를 거꾸로 해놨다고 b tree 라고 .

뿌리-가지-

3에서 love acd acd rowid

 

 

 

문제1.

사원 테이블에 이름에 인덱스를 걸고

 emp_ename 인덱스의 데이터를 가져오게끔 아래와 같이

수행하시오

 

create index emp_ename

 on emp(ename);

 

select ename, rowid

 from emp

 where ename > '  ';

 

 

 

 

문제2.

emp_ename  b*tree 인덱스의 leaf 블럭이 개가 있는지

확인하시오

 

analyze index emp_ename

 validate structure;

 

select name, lf_rows, del_lf_rows

 from index_stats;

 

 

 

내용이

root - branch - leaf 구조를 타고 내려오는데

14 leaf

adams..allen ..............ward 순으로 나온다.

문제1 답이 그렇게 나온거다.

 

 

 

문제3.

이름이 SCOTT 사원의 이름과 월급과 직업을 출력하는데

인덱스를 통해서 테이블을 엑세스 하는지 실행계획을 보고

확인하시오

 

위에 ename 으로 인덱스 한거 있음.

 

select ename, sal, job

from emp

 where ename = 'SCOTT'; 

아래서부터 작업이 실행되 올라감.

rowid 거쳐서 가는걸 있음.

 

 

 

문제4.

이름이 scott 사원의 이름과 월급과 직업을 출력하는

sql 작성하는데

full table scan 되게 힌트를 주시오.

 

select /*+ full(emp) */ ename, sal, job

 from emp

 where ename = 'SCOTT';

 

 

 

문제5. 위의 sql full table scan 실행계획과

인덱스 스캔 실행계획의 블럭의 차이가 얼마나 발생하는지

확인하시오

 

튜닝전:

select /*+ full(emp) */ ename, sal, job

 from emp

 where ename = 'SCOTT';

 

 

튜닝후:

select  /*+ index (emp emp_ename) */

ename, sal, job

from emp

 where ename = 'SCOTT'; 

 

 

 

문제6. 오라클 교육용 데이터중에 가장 sales 테이블을

가지고 아래의 테이블을 생성하시오!

 

create table sales100

as

 select rownum empno, s.*

 from sales s;

 

 

 

문제7. sales100 사원번호가 56071 번인 사원의

모든 컬럼을 출력하는 쿼리를 작성하고

읽어들인 블럭의 갯수를 확인하시오

 

select *

from sales100

where empno = 56071;

 

cmd 에서

set autot traceonly expain;

 

블럭수 5600 확인가능

 

 

문제8. sales100 사원번호에 인덱스를 걸고

 위의 sql 실행해서 인덱스 스캔을 하는지 확인을 하고

블럭의 갯수를 확인하시오

 

create index sales100_empno

on  sales100(empno);

 

select *

from sales100

where empno = 56071;

 

블럭수가 42개로 줄어든다.

 

 

 

문제9. KING 이름을 JACK 으로 변경하시오

(ENAME 인덱스 걸어 상태임)

*INDEX 처리상황을 이해하기 위한 문제*

 

update emp

 set ename = 'JACK'

where ename = 'KING';

 

commit;

 

테이블의 경우는 데이터가 KING ->  JACK 으로

바로 변경이 되나

인덱스는 KING 리프블럭(LEAF BLOCK)

                    그냥 두고(검게, 죽은이파리화)

            JACK 이라는 리프블럭을 추가한다.(abcd순서에 맞게)

 

analyze index emp_ename

 validate structure;

 

select name, lf_rows, del_lf_rows

 from index_stats;

이걸 돌려보면

잭이 추가되어

14+1=15 되었고

죽은 이파리 하나 생긴걸 있다.

 

계속 쌓이니까 가지치기, 재정리를 해준다

 

*인덱스 재구성 명령어

alter index emp_ename rebuild;

 

다시 인덱스 확인하기 위해

analyze index emp_ename

 validate structure;

 

select name, lf_rows, del_lf_rows

 from index_stats;

**validate : 유효성을 검사하다

 

죽은이파리가 사라진걸 있다.

 

 

index range scan : /*+ index(테이블명 인덱스명) */

문제10. 월급이 3000 사원의 이름과 월급을 출력하는

sql 작성하는데

실행계획이 index range scan 되도록 인덱스를 걸고

작성하시오

 

create index emp_sal

on emp(Sal);

 

select /*+ index(emp emp_sal) */

ename, sal

from emp

where sal = 3000;

 

 

목차-페이지

인덱스-rowid

sal 걸어둔 인덱스 찾아서 3000 검색

테이블로 가서 3000 대한 rowid 검색

그리고 출력

 

문제10.(점심시간문제)

인덱스가 목차찾고 rownum 찾는 증명하시오

 

select /*+ index asc (emp emp_sal) */

sal, rowid

from emp

where sal>0;

 

 select rowid, ename

 from emp;

 

 

 

 

 

문제11. 우리반 테이블에 이름에 인덱스를 걸고

아래의 sql 인덱스를 통해서 어떻게 테이블의

데이터를 엑세스 하는지 그림으로 그리시오

 

create index emp2_ename

on emp2(ename);

 

 

select ename, age, major

 from emp2

 where ename = '서일'

 

select ename, rowid

from emp2

where ename > ' ';

 

emp2_ename 인덱스 --------------------emp2 테이블

 

 

문제12.

통신사 컬럼에 인덱스를 생성하시오!

 

create index emp2_telecom

on emp2(telecom);

 

 

 

문제13.

이름이 서일이고 통신사가 sk 학생의

이름,통신사,나이,전공을 출력하시오

 

select ename, lower(telecom), age, major

from emp2

where ename = '서일' AND lower(telecom) = 'sk'

 

*현재 이름,텔레콤에 인덱스가 설정되어있다.

위의 2개의 인덱스 중에 어느 컬럼의 인덱스를 사용할까?

이름이다.

왜냐하면

이름 컬럼에 중복내용이 없으니까.

 

 

문제14. 아래의 sql 인덱스가 한번은 ename 인덱스를 타게

힌트를 주고

한번은 telecom 인덱스를 타게끔 힌트를 주시오.

select ename, lower(telecom), age, major

from emp2

where ename = '서일' AND lower(telecom) = 'sk'

 

 

select /*+ index (emp2 emp2_name) */

     ename, lower(telecom)

from emp2

where ename = '서일' AND lower(telecom) = 'sk';

 

 

 

select /*+ index (emp2 emp2_telecom) */

     ename, telecom

from emp2

where ename = '서일' AND telecom = 'sk';

 

힌트를 통해서 제어를 한다.

***lower 빼니까 telecom 타서 인덱스돌아감.

lower(telecom) 갖고오는건지

함수를 쓰라는건지 컴퓨터가 없음.

 

 

문제15.아래의 sql 튜닝하시오

튜닝전: select ename, telecom, age

           from emp2

         where lower(telecom) = 'sk';

 

튜닝후:

update emp2

set telecom = 'sk'

where telecom = 'SK'; 

 

select ename, telecom, age

           from emp2

         where telecom = 'sk';

 

***lower 지운게 튜닝....

 

****point 좌변을 가공하지 말아라

 

 

문제16. 아래의 sql 튜닝하시오

튜닝전:

select ename, age, telecom

 from emp2

 where telecom||age='sk26';

 

튜닝후:

select ename, age, telecom

from emp2

where telecom ='sk'

and

       age = 26;

알아서 텔레콤에 걸어둔 인덱스 타고 작업함.

 

 

자리마다 실행계획이 틀려지는 이유는?

자리마다 옵티마이져가 만드는 실행계획이 달라서 이다.

다르냐면?

   옵티마이져에게 줘야하는 정보가 부족해서 그렇다

emp2 테이블에 대한 분석정보를 생성해서 알려줘야 한다.

 

-emp2 대해서 분석하겠다.

analyze table emp2 compute statistics;

 

-emp2 테이블의 분석정보가 언제 생성되었는지

  확인하는 방법

select table_name, last_analyzed

 from user_tables;

 

last_analyzed 날짜값 나오면 분석된거임.

분석정보를 갱신해서 줘야

옵티마이져가 수행을 해준다.

최신분석정보 안주면 말을 들어서 힌트 주고 그래야함.

 

 

 

문제17. 아래의 sql 튜닝하시오

(full table scan 아니라 index range scan 되게 하세요)

튜닝전:

select ename, sal, hiredate

     from emp

   where to_char(hiredate, 'rr/mm/dd') = '81/11/17';

 

튜닝후:

create index emp_hiredate

on emp(hiredate);

 

select ename, sal, hiredate

 from emp

 where hiredate = to_date('81/11/17', 'rr/mm/dd');

 

 

결합 컬럼 인덱스

 하나의 컬럼으로 인덱스를 구성한게 아니라

 여러개의 컬럼으로 인덱스를 구성한

 

:

 create index emp_deptno_sal

 on emp(deptno, sal);

 

select deptno, sal, rowid

 from emp

 where deptno > 0;

테이블 스캔안하고

인덱스만 스캔 있다.

결과 자체가 인덱스 수행 내역이라고 보면 된다.

 

 

 

문제18. 아래의 sql 튜닝하시오

(그룹함수 쓰지 말고 인덱스로만

원하는 결과를 있게 하시오)

튜닝전: select deptno, max(sal)

           from emp

         where deptno = 20

          group by deptno;

 

 

 

튜닝후:

select /*+ index_desc (emp emp_deptno_sal) */

        deptno, sal

 from emp

 where deptno = 20

 and

 rownum = 1;

 

 

예전거

튜닝전:

select ename, sal

 from emp

 where sal = (select max(Sal)

                  from emp);

 

닝후:

select /*+ index_desc (emp emp_sal) */

        ename, sal

 from emp

 where deptno = 20

 and

 rownum = 1;

 

 

문제19. 아래의 sql 튜닝하시오

(필요한 인덱스 알아서 생성하시오)

튜닝전:

select job, min(sal)

 from emp

 where job= 'SALESMAN'

group by job;

 

튜닝후:

create index emp_job_sal

on emp(job,sal);

 

select /*+ index_asc (emp emp_job_sal) */

 job, sal

 from emp

 where job = 'SALESMAN'

 and

 rownum =1;

 

 

 

문제20. 아래의 sql 튜닝하시오

튜닝전:

select deptno, sum(sal)

 from emp

 group by deptno;

 

튜닝후:

  -index full scan:      /*+ index_fs(테이블명 인덱스명) */

   -index fast full scan: /*+ index_ffs(테이블명 인덱스명) */

2 같은건데 ffs 빠릅니다.

 

select /*+ index_ffs (emp emp_deptno_sal) */

deptno, sum(sal)

from emp

group by deptno;

인덱스 안탐..........................................

index fast full scan 되려면 deptno 또는 sal

 not null 제약 걸려 있어야 한다.

책으로 따지면 빈페이지가 없음을 보장해줘야 한다.

 

      alter table emp

        modify deptno not null;

 

select /*+ index_ffs (emp emp_deptno_sal) */

deptno, sum(sal)

from emp

group by deptno;

 

where deptno is not null 이라고 줘도 된다.

 

F7 확인하면 빨간게 뜨긴 하는데

index full scan 이라고 나온다.

 

 

문제21.

아래의 sql 튜닝하시오

(인덱스도 알아서)

 

튜닝전:

select job, count(*)

 from emp

 group by job;

 

튜닝후:

create index emp_job

 on emp(job);

 

alter table emp

 modify job not null;

 

select /*+ index_ffs (emp emp_job) */

    job, count(*)

 from emp

group by job;

 

 

결합 컬럼 인덱스의 특징

" 결합 컬럼 인덱스의 첫번째 컬럼이

 where 절에 존재해야

 결합 컬럼 인덱스를 엑세스 있다."

 

*scott 유저가 가지고 있는 인덱스 리스트 조회

 

select index_name, column_name, column_position

 from user_ind_columns

 where table_name = 'EMP'

  order by index_name, column_position;

 

drop table emp 해서 날리고

다시 생성

 

 

문제22. 월급이 1250 사원의 이름,월급, 부서번호를

 출력하고 실행계획을 확인하시오

 

create index emp_deptno_sal

   on emp(deptno, sal);

 

select ename, sal, deptno

from emp

where sal = 1250;

이렇게 하면 풀스캔 뜬다

인덱스 생성시 deptno, sal 순서로 만들었다.

그래서 range index

where 절에 deptno 넣을 때만 인덱스 탄다.

이걸 가능하게 하는 방법은

index skip scan !!!!!

 

 

select /*+ index_ss(emp emp_deptno_sal) */

ename, sal, deptno

from emp

where sal = 1250;

 

 

select detpno, sal, rowid

 from emp

 where sal =1250;

 

 

*index skip scan*

deptno_sal

where =1250

  skip scan 돌리면

부서번호마다 돌면서 1250 찾는다

그러다

부서번호 30에서

1250 찾으면 작업중이던 부서번호 30에서는 나간다.

(중복을 배제 하는거 아님)

부서번호가 10,20,30 .....50 까지 있으면

50까지는 1250 찾는다.

 

 

 

문제23. 아래 sql 튜닝하시오

 

drop index emp_deptno_sal;

 

create index emp_job_sal

 on emp(job, sal);

 

인덱스의 구조

 

 

 

튜닝전

select ename, job, sal, deptno

 from emp

 where sal = 3000;

 

튜닝후

select  /*+ index_ss (emp emp_job_sal) */

ename, job, sal, deptno

 from emp

 where sal = 3000;

 

 

 

문제24.(오늘의 마지막 문제)

 demobld 돌리고 이름에 index 거시오

 그리고 아래의 sql 튜닝하시오

 

튜닝전:

select ename, sal, job, deptno

 from emp

 where ename like '%EN%'

        or

             ename like '%IN%';

 

 

튜닝후:

create index emp_ename

 on emp(ename);

 

1. 이름에 EN 또는 IN 포함하고 있는 사원들의

데이터의 ROWID EMP_ENAME 에서

빠르게 스캔해서 가져오는

쿼리를 만든다.

 

2. 튜닝전의 쿼리를 in line view 만들고

rowid 연결고리로 해서

emp 조인한다.

 

alter 써서 하는 방법:

alter table emp

 modify ename not null;

 

select ename, sal, job, deptno

 from emp e,

     (select /*+ index_ffs(emp emp_ename) */

         rowid, ename

      from emp

    where ename like '%EN%'

       or

                 ename like '%IN%') ee

where e.rowid = ee.rowid;

 

 

 

where 절에 인덱스 조건 거는 방법:

select e1.ename, e1.sal, e1.job, e1.deptno

from emp e1, ( select /*+ index_ffs(emp emp_ename) */ rowid

from emp

where ename like '%EN%'

or ename like '%IN%' ) e2

where e1.rowid = e2.rowid

and

 ename > ' ';

 

 

 

선생님의 merge 인덱스 방법:

select /*+ rowid(e) use_nl(v e) */

         e.ename, e.sal, e.job, e.deptno

 from emp e,

         (select /*+ no_merge index_ffs(emp emp_ename) */

                 rowid as rn

               from emp

              where ename like '%EN%'

                   or   ename like '%IN%') v

where e.rowid = v.rn;

 

※ no_merge 힌트?

  in line view 해체하지 말아라

 

 

 

728x90
반응형

'sql_tuning' 카테고리의 다른 글

5. 파티셔닝  (0) 2019.04.02
4. 서브쿼리 튜닝  (0) 2019.04.02
3. 조인 튜닝  (0) 2019.04.02
2-1. 인덱스 엑세스 방법  (0) 2019.04.02
1. sql 튜닝이란 무엇이고 왜 배워야 하는가?  (0) 2019.04.02