Oracle/index

index(인덱스)

pbj1102 2022. 1. 24. 14:53
반응형

인덱스

인덱스란? 
- 검색을 빠르게 하기 위해 사용
- 인덱스 구성시 자동정렬이 이루어짐

- 사용을 해야하는 경우
. 테이블에 행의 수가 많을때
. 조건절인 where 문에 column 수가 많을 때
. join , null 포함하는 컬럼이 많을 떄

- 사용하지 말아야 하는 경우
. 테이블의 행의 수가 적을 때
. where 문에 해당 컬럼이 자주 사용되지 않을 때
. 테이블의 DML 작업이 많은 경우

- 확인
. USER_INDEXES : 인덱스 이름 및 고유성 
. USER_IND_COLUMNS : 인덱스 이름, 테이블 이름, 열 이름


-index 생성법
. create index index명 on table명(index걸어줄 table에 colum명);
  ex)create index idx_emp_ename on emp(ename);

-index 제거
. drop index index명
  ex)drop index idx_emp_ename;

-index 재생성
. alter index index명 rebuild;
  ex)alter index idx_emp_ename rebuild;

. DML 같은 작업이 많을 경우 효율성이 떨어지게 된다. 이때 rebuild 필요

-index 조회(index를 모두 보여준다.)
. select object_name, object_type from user_objects where object_type ='INDEX';


-인덱스 종류
. Unique Index(고유 인덱스) - 유일한 값을 갖는 컬럼에 대해서 생성하는 인덱스
  . NonUnique Index(비고유 인덱스) - 중복된 데이터를 갖는 컬럼에 대해서 생성하는 인덱스
. Single Index(단일 인덱스)
. Composite Index(결합 인덱스)

- 결합 인덱스 우선순위
. 1순위 - 컬럼이 사용한 연산자에 의한 인덱스 컬럼 선정 
. 2순위 - 랜덤 액세스를 고려한 인덱스 컬럼 선정 
. 3순위 - 정렬 제거를 위한 인덱스 컬럼 선정 
. 4순위 - 단일 컬럼의 분포도를 고려한 인덱스 컬럼 선정 
 



# 자동정렬 확인 

create table idx_test(
  id varchar(10) not null,
  sid varchar(10) not null,
  suid varchar(10) not null);


  create table idx_test(
  id varchar(10) not null,
  sid varchar(10) not null,
  suid varchar(10) not null);
  
  insert into idx_test values ('3','1','3');
  insert into idx_test values ('3','2','1');
  insert into idx_test values ('3','2','2');
  insert into idx_test values ('3','2','3');
  insert into idx_test values ('3','3','3');
  insert into idx_test values ('1','1','1');
  insert into idx_test values ('2','3','2');
  insert into idx_test values ('2','3','3');
  insert into idx_test values ('3','1','1');
  insert into idx_test values ('1','3','2');
  insert into idx_test values ('1','3','3');
  insert into idx_test values ('2','1','1');
  insert into idx_test values ('2','2','1');
  insert into idx_test values ('2','2','2');
  insert into idx_test values ('2','2','3');
  insert into idx_test values ('2','3','1');
  insert into idx_test values ('3','3','1');
  insert into idx_test values ('3','3','2');
  insert into idx_test values ('3','1','2');
  insert into idx_test values ('1','1','2');
  insert into idx_test values ('1','1','3');
  insert into idx_test values ('1','2','1');
  insert into idx_test values ('1','2','2');
  insert into idx_test values ('1','2','3');
  insert into idx_test values ('1','3','1');
  insert into idx_test values ('2','1','2');
  insert into idx_test values ('2','1','3');
  
  
// 뒤죽박죽으로 들어간 것을 확인

SQL> select * from IDX_TEST;

ID   SID  SUID
---- ---- ----
3    1    3
3    2    1
3    2    2
3    2    3
3    3    3
1    1    1
2    3    2
2    3    3
3    1    1
1    3    2
1    3    3

ID   SID  SUID
---- ---- ----
2    1    1
2    2    1
2    2    2
2    2    3
2    3    1
3    3    1
3    3    2
3    1    2
1    1    2
1    1    3
1    2    1

ID   SID  SUID
---- ---- ----
1    2    2
1    2    3
1    3    1
2    1    2
2    1    3



// 인덱스 생성
create index idx on idx_test(id,sid,suid);


//인덱스 확인
SQL> select a.index_name , a.table_name , b.column_name, b.DESCEND
     from user_indexes a, user_ind_columns b
     where a.index_name=b.index_name 
     and a.index_name='IDX';

INDEX_NAME      TABLE_NAME      COLUMN_NAM DESC
--------------- --------------- ---------- ----
IDX             IDX_TEST        ID         ASC
IDX             IDX_TEST        SID        ASC
IDX             IDX_TEST        SUID       ASC



// 인덱스 (정렬된 데이터 확인) 
SQL>  select * from IDX_TEST;

ID   SID  SUID
---- ---- ----
1    1    1
1    1    2
1    1    3
1    2    1
1    2    2
1    2    3
1    3    1
1    3    2
1    3    3
2    1    1
2    1    2

ID   SID  SUID
---- ---- ----
2    1    3
2    2    1
2    2    2
2    2    3
2    3    1
2    3    2
2    3    3
3    1    1
3    1    2
3    1    3
3    2    1

ID   SID  SUID
---- ---- ----
3    2    2
3    2    3
3    3    1
3    3    2
3    3    3

27 rows selected.





select * from idx_test 
where id = '3' and sid='2' and suid='1';













create table idx_test2(
  id varchar(10) not null,
  sid varchar(10) not null,
  suid varchar(10) not null);

  
  insert into idx_test2 values ('3','1','3');
  insert into idx_test2 values ('3','2','1');
  insert into idx_test2 values ('3','2','2');
  insert into idx_test2 values ('3','2','3');
  insert into idx_test2 values ('3','3','3');
  insert into idx_test2 values ('1','1','1');
  insert into idx_test2 values ('2','3','2');
  insert into idx_test2 values ('2','3','3');
  insert into idx_test2 values ('3','1','1');
  insert into idx_test2 values ('1','3','2');
  insert into idx_test2 values ('1','3','3');
  insert into idx_test2 values ('2','1','1');
  insert into idx_test2 values ('2','2','1');
  insert into idx_test2 values ('2','2','2');
  insert into idx_test2 values ('2','2','3');
  insert into idx_test2 values ('2','3','1');
  insert into idx_test2 values ('3','3','1');
  insert into idx_test2 values ('3','3','2');
  insert into idx_test2 values ('3','1','2');
  insert into idx_test2 values ('1','1','2');
  insert into idx_test2 values ('1','1','3');
  insert into idx_test2 values ('1','2','1');
  insert into idx_test2 values ('1','2','2');
  insert into idx_test2 values ('1','2','3');
  insert into idx_test2 values ('1','3','1');
  insert into idx_test2 values ('2','1','2');
  insert into idx_test2 values ('2','1','3');
  
  
  
  create index id_idx on idx_test(id);
  
  
  commit;
  
  select a.index_name , a.table_name , b.column_name, b.DESCEND
     from user_indexes a, user_ind_columns b
     where a.index_name=b.index_name 
     and a.TABLE_name LIKE 'IDX_%'

INDEX_NAME      TABLE_NAME      COLUMN_NAM DESC
--------------- --------------- ---------- ----
IDX2            IDX_TEST2       ID         ASC
IDX             IDX_TEST        SUID       ASC
IDX             IDX_TEST        SID        ASC
IDX             IDX_TEST        ID         ASC






SQL> select count(*) from idx_test;

  COUNT(*)
----------
      5500

SQL> select count(*) from idx_test2;

  COUNT(*)
----------
      5500




  select a.index_name , a.table_name , b.column_name, b.DESCEND
     from user_indexes a, user_ind_columns b
     where a.index_name=b.index_name 
     and a.TABLE_name LIKE 'IDX_%'

INDEX_NAME      TABLE_NAME      COLUMN_NAM DESC
--------------- --------------- ---------- ----
ID_IDX         IDX_TEST2       ID         ASC
IDX             IDX_TEST        SUID       ASC
IDX             IDX_TEST        SID        ASC
IDX             IDX_TEST        ID         ASC




실행계획 결과 

select * from idx_test 
where id = '3' and sid='2' and suid='432';
==> INDEX (RANGE SCAN) OF 'IDX'(INDEX)(Cost=1 Card=1 Bytes=21)
==> where 문에 인덱스 칼럼이 모드 들어간 경우 인덱스를 잘탐





select * from idx_test2
where id = '3' and sid='2' and suid='432';

===>> TABLE ACCESS (FULL) OF 'IDX_TEST2(TABLE)(Cost=5 Card=1 Bytes=21)
==> where 문에 index 문이 하나가 들어간 경우 full scan





select * from idx_test 
where sid='2' and suid='432';

===>> TABLE ACCESS (FULL) OF 'IDX_TEST(TABLE)(Cost=5 Card=3 Bytes=63)
==> where 문에 인덱스 중간부터 들어간 경우 full scan













# 함수 인덱스




create table fidx_test(
  id varchar(10) not null,
  sid varchar(10) not null,
  suid varchar(10) not null);

  create index FIDX on FIDX_TEST(id);
  COMMIT;
  

 
 select column_name, last_analyzed from user_tab_cols where table_name = 'FIDX_TEST';


  select a.index_name , a.table_name , b.column_name, b.DESCEND
     from user_indexes a, user_ind_columns b
     where a.index_name=b.index_name AND
        B.TABLE_NAME='FIDX_TEST';

INDEX_NAME      TABLE_NAME      COLUMN_NAM DESC
--------------- --------------- ---------- ----
FIDX            IDX_TEST       SID         ASC
FIDX            IDX_TEST       ID         ASC



select ID,SID from FIDX_TEST
WHERE ID='a' AND SID='3'
==> INDEX (RANGE SCAN) OF 'FIDX'(INDEX)(Cost=2 Card=500 Bytes=7K)




select substr(id,3,1) ,sid 
from FIDX_TEST
WHERE ID='asd' and SID='3';
==> TABLE ACCESS (FULL) OF 'FIDX_TEST'(table)(Cost=3, Card=5000 Bytes=7k)





==> 쿼리문에 함수 즉, 인덱스를 갖는 칼럼의 값에 변경이 일어나는 경우
where 문이 같더라도 index 를 타지 않는 것을 확인




drop index FIDX




create index FIDX_F on FIDX_TEST(substr(id,'3','1'),sid);


SQL>   select a.index_name , a.table_name , b.column_name, b.DESCEND
        from user_indexes a, user_ind_columns b
         where a.index_name=b.index_name AND
        B.TABLE_NAME='FIDX_TEST';

INDEX_NAME TABLE_NAME      COLUMN_NAME     DESC
---------- --------------- --------------- ----
FIDX_F     FIDX_TEST       SYS_NC00004$    ASC
FIDX_F     FIDX_TEST       SID             ASC





select substr(id,3,1) ,sid 
from FIDX_TEST
WHERE ID='asd' and SID='3';
==>INDEX (fast scan )

인덱스를 타지만 FAST FULL SCAN 을 사용함










#인덱스 Rebuild

DELETE FROM IDX_TEST WHERE ID='2' AND SID='3'


alter index idx REBUILD ;




############### 인덱스가 얼마나 손상이 됬는지 확인 하기위해 
                index_stats 뷰를 인덱스 정보로 채운다



ANALYZE INDEX IDX VALIDATE STRUCTURE



SELECT blocks, pct_used, distinct_keys,lf_rows, del_lf_rows
 FROM index_stats;
 
 
BLOCK  PCT_USED  DISTINCT_KEYS LF_ROWS  DEL_LF_ROWS
16 82   4500         4500    1500



alter index idx REBUILD ONLINE; 




ANALYZE INDEX IDX VALIDATE STRUCTURE

SELECT blocks, pct_used, distinct_keys,lf_rows, del_lf_rows
 FROM index_stats;


BLOCK  PCT_USED  DISTINCT_KEYS LF_ROWS DEL_LF_ROWS
16 79   3000 3000  0



###  DEL_LF_ROWS 값이 줄어듬  













##################

힌트를 사용하기 위해
SCOTT 계정 으로 테스트

-----------------------------------------
EXPLAIN PLAN 권한이 없으므로 권한부여

롤 plus 생성
plus 롤에 부여할 객체권한 부여
사용자 scott 에게 만든 plus 권한 부여
-----------------------------------------


sys 계정


create index idx_emp_hiredate on emp(hiredate);
create index idx_emp_sal on emp(sal);
create index idx_emp_ename on emp(ename);
select * from dba_indexes where table_name='EMP';



create role plus;

grant select on v_$sesstat to plus;
grant select on v_$statname to plus;
grant select on v_$mystat to plus;
grant plus to dba with admin option;



grant plus to scott;



SQL> SELECT * FROM DBA_ROLE_PRIVS WHERE GRANTEE='SCOTT';

GRANTEE                        GRANTED_ROLE                   ADM DEF
------------------------------ ------------------------------ --- ---
SCOTT                          RESOURCE                       NO  NO
SCOTT                          HR_CLERK                       NO  NO
SCOTT                          PLUS                           NO  NO
SCOTT                          CONNECT                        NO  NO






SQL> select * from role_tab_privs where role ='PLUS';

ROLE    OWNER   TABLE_NAME      COLUMN_NAM PRIVILEGE  GRA
------- ------- --------------- ---------- ---------- ---
PLUS    SYS     V_$SESSTAT                 SELECT     NO
PLUS    SYS     V_$MYSTAT                  SELECT     NO
PLUS    SYS     V_$STATNAME                SELECT     NO



select index_name, table_owner, table_name from user_indexes where table_name='EMP';

INDEX_NAME                     TABLE_OWNER                    TABLE_NAME
------------------------------ ------------------------------ ------------------------------
IDX_EMP_ENAME                  SYS                            EMP
IDX_EMP_SAL                    SYS                            EMP
IDX_EMP_HIREDATE               SYS                            EMP








-----------------------
sqlplus 에서 EXPLAIN PLAN 실행 하려만 아래 두 명령어중 하나 실행 ---> 근데 보기가 불편
set autot on     
set autot trace exp

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

아래는 오렌지에서 확인한것



select 
    ename, sal, hiredate
from emp
where  to_char(hiredate,'yymmdd')='810220'
and sal > 1200
and deptno = 30
and ename like '%ALLEN%';

===> table access (full) OF 'EMP' (TABLE) (Cost=2 Card=1 Bytes=32)
===> index 를 타지 못함





select /*+ index(emp idx_emp_sal) */
    ename, sal, hiredate
from emp
where  to_char(hiredate,'yymmdd')='810220'
and sal > 1200
and deptno = 30
and ename like '%ALLEN%';
===> INDEX (RANGE SCAN) OF 'IDX_EMP_SAL' (INDEX)(Cost=1 Card=11)
===> EMP  idx_emp_sal의 인덱스 힌트를 주었더니 index 를 타기 시작함




select /*+ index(emp idx_emp_hiredate) */
    ename, sal, hiredate
from emp
where  to_char(hiredate,'yymmdd')='810220'
and sal > 1200
and deptno = 30
and ename like '%ALLEN%';
==>  TALBE ACCESS (FULL) OF 'EMP' (TABLE) (Cost=2 Card=1 Bytes=42)
==>  HIREDATE 값이 변경되서 못타는듯 ? => TO_CHAR 함수때문에 






select /*+ index(emp idx_emp_hiredate) */
    ename, sal, hiredate
from emp
where hiredate='810220'
and sal > 1200
and deptno = 30
and ename like '%ALLEN%';
===> INDEX (RANGE SCAN) OF 'IDX_EMP_HIREDATE' (INDEX)(Cost=1 Card=1)
===> hiredate 쿼리문을 변경 함






select /*+ index(emp idx_emp_ename) */
    ename, sal, hiredate
from emp
where hiredate='810220'
and sal > 1200
and deptno = 30
and ename like '%ALLEN%';
===> INDEX ( FULL SCAN) OF 'IDX_EMP_ENAME(INDEX_ ( Cost=1 Card=1 )
==> 인덱스를 타지만 풀스캔임 ==> ename 구문이 like 로 검색을 해서 그런거 같음
==> Optimizer 에서 INDEX 태운다음 풀스캔이 빠르다고 판단을 한것. 



select /*+ index(emp idx_emp_ename) */
    ename, sal, hiredate
from emp
where hiredate='810220'
and sal > 1200
and deptno = 30
and ename = 'ALLEN';
===> INDEX (RANGE SCAN) OF 'IDX_EMP_ENAME' (INDEX)(Cost=1 Card=1)
===> ename 쿼리문을 변경 함




Cost 와 Card 값을 보면 
인덱스 힌드를 ename, hiredate 로 줄 경우 Cost=1 Card=1
sal 는 Cost=1 Card=11  다른것을 확인

이유는 where 문에서 sal > 1200 으로 설정 했기 때문


sal > 1200  인것이 11개가 있는거 같음 



확인해보쟈




SQL> select * from EMP
  2  where sal > 1200;

     EMPNO ENAME      JOB              MGR HIREDATE            SAL       COMM     DEPTNO
---------- ---------- --------- ---------- ------------ ---------- ---------- ----------
      7499 ALLEN      SALESMAN        7698 20-FEB-81          1600        300         30
      7521 WARD       SALESMAN        7698 22-FEB-81          1250        500         30
      7566 JONES      MANAGER         7839 02-APR-81          2975                    20
      7654 MARTIN     SALESMAN        7698 28-SEP-81          1250       1400         30
      7698 BLAKE      MANAGER         7839 01-MAY-81          2850                    30
      7782 CLARK      MANAGER         7839 09-JUN-81          2450                    10
      7788 SCOTT      ANALYST         7566 19-APR-87          3000                    20
      7839 KING       PRESIDENT            17-NOV-81          5000                    10
      7844 TURNER     SALESMAN        7698 08-SEP-81          1500          0         30
      7902 FORD       ANALYST         7566 03-DEC-81          3000                    20
      7934 MILLER     CLERK           7782 23-JAN-82          1300                    10

11 rows selected.

 위와 같이 sal > 1200 값이 11개였음










###### to_char(hiredate,'yymmdd')  << 함수인덱스 만들어서 인덱스 검색 하기

select /*+ index(emp idx_emp_hiredate) */
    ename, sal, hiredate
from emp
where  to_char(hiredate,'yymmdd')='810220'
and sal > 1200
and deptno = 30
and ename like '%ALLEN%';
==>  TALBE ACCESS (FULL) OF 'EMP' (TABLE) (Cost=2 Card=1 Bytes=42)
==>  HIREDATE 값이 변경되서 못타는듯 ? => TO_CHAR 함수때문에 







create index idx_emp_to_hiredate on emp(TO_CHAR(hiredate),'yyddmm');



SQL> select LAST_ANALYZED, index_type ,index_name, table_owner, table_name  from user_indexes where table_name='EMP';

INDEX_TYPE                  INDEX_NAME           TABLE_OWNER     TABLE_NAME
--------------------------- -------------------- --------------- ---------------
FUNCTION-BASED NORMAL       IDX_EMP_TO_HIREDATE  SYS             EMP
NORMAL                      IDX_EMP_ENAME        SYS             EMP
NORMAL                      IDX_EMP_SAL          SYS             EMP
NORMAL                      IDX_EMP_HIREDATE     SYS             EMP


select /*+ index(emp idx_emp_hiredate) */
    ename, sal, hiredate
from emp
where  to_char(hiredate,'yymmdd')='810220'
and sal > 1200
and deptno = 30
and ename like '%ALLEN%';



==>  TALBE ACCESS (FULL) OF 'EMP' (TABLE) (Cost=2 Card=1 Bytes=42)
===>> 걸려있는데 못탐!!




이유 : 오라클에서 함수 인덱스에 대한 통계정보를 아직 이루지 못했기 때문 
함수 인덱스 생성시 -> ANALYZE 자동으로 이루이지지 않음

ANALYZE INDEX IDX_EMP_TO_HIREDATE VALIDATE STRUCTURE   << 명령어로 ANALYZE 실행

ANALYZE 시간은 user_indesx 테이블에 확인 가능



ANALYZE INDEX IDX_EMP_TO_HIREDATE VALIDATE STRUCTURE 

select LAST_ANALYZED, index_type ,index_name, table_owner, table_name  from user_indexes where table_name='EMP';
LAST_ANALYZED
2016/01/15 18:12:16 





select /*+ index(emp idx_emp_hiredate) */
    ename, sal, hiredate
from emp
where  to_char(hiredate,'yymmdd')='810220'
and sal > 1200
and deptno = 30
and ename like '%ALLEN%';

===> INDEX (RANGE SCAN) OF 'IDX_EMP_TO_HIREDATE' (INDEX)(Cost=1 Card=1)
==> 함수 인덱스를 타기 시작함 



######################################################
*** 즉 함수 인덱스를 사용시 
ANALYZE 를 항상 해줘야 함수인덱스를 탐 



반응형

'Oracle > index' 카테고리의 다른 글

인덱스 분포도  (0) 2022.01.24
index frag_blocks check  (0) 2022.01.24
partition global index 추출  (0) 2022.01.24
index invisible  (0) 2022.01.24
index monitoring  (0) 2022.01.24