DATABASE(oracleDB 11g)/Tuning

[SQL Tuning]인덱스(Index)의 기초

SEUNGSAMI 2019. 5. 20. 13:52
인덱스(Index)의 기초



인덱스
  • 양방향 링크가 되어있으므로, range scan이 가능



B*트리 인덱스 
  • 가장 기본적인 인덱스



비트맵 인덱스
  • DML이 덜 일어나도록 하는 DW(data warehouse)
  • and와 or이 많은 경우
  • cardinality를 줄여야 할 경우

USER01@orcl2> select count(*) from custs;

  COUNT(*)
----------
     55500


USER01@orcl2> select count(*) from sales;

  COUNT(*)
----------
    918843




USER@orcl2> select count(distinct(cust_city)) from custs;

COUNT(DISTINCT(CUST_CITY))
--------------------------
                       620

-- 620개의 비트맵을 만들어야 하므로, 인덱스의 사이즈가 작지는 않다.


USER01@orcl2> select sum(amount_sold)
              from sales s, custs c
              where s.cust_id = c.cust_id
              and cust_city = 'Berlin';
              

USER01@orcl2> @xplan
-- HASH 조인을 진행하고 테이블 풀 스캔을 한다는 것을 알 수 있다.


USER01@orcl2> select sum(amount_sold)
              from sales s, custs c
              where s.cust_id = c.cust_id
              and cust_city = 'Berlin';


USER01@orcl2> @xplan_all
---------------------------------------------------------------------------------------------------------------------------
| Id  | Operation           | Name  | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  |  OMem |  1Mem | Used-Mem |
----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |       |      1 |        |      1 |00:00:04.73 |    5563 |   4431 |       |       |         |
|   1 |  SORT AGGREGATE     |       |      1 |      1 |      1 |00:00:04.73 |    5563 |   4431 |       |       |         |
|*  2 |   HASH JOIN         |       |      1 |  11652 |    318 |00:00:04.73 |    5563 |   4431 |  1036K|  1036K| 1185K (0)|
|*  3 |    TABLE ACCESS FULL| CUSTS |      1 |     90 |     50 |00:00:00.01 |    1128 |      0 |       |       |         |
|   4 |    TABLE ACCESS FULL| SALES |      1 |    918K|    918K|00:00:01.21 |    4435 |   4431 |       |       |         |
----------------------------------------------------------------------------------------------------

Outline Data
-------------

  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('11.2.0.1')
      DB_VERSION('11.2.0.1')
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$1")
      FULL(@"SEL$1" "C"@"SEL$1")
      FULL(@"SEL$1" "S"@"SEL$1")
      LEADING(@"SEL$1" "C"@"SEL$1" "S"@"SEL$1")
      USE_HASH(@"SEL$1" "S"@"SEL$1")
      END_OUTLINE_DATA
  */

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("S"."CUST_ID"="C"."CUST_ID")
   3 - filter("CUST_CITY"='Berlin')



USER01@orcl2> create bitmap index cust_sales_bji
              on sales(c.cust_city)
              from sales s, custs c
              where c.cust_id = s.cust_id;
              

USER01@orcl2> select sum(amount_sold)
              from sales s, custs c
              where s.cust_id = c.cust_id
              and cust_city = 'Berlin';
              
              
USER01@orcl2> @xplan_all              
----------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name           | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  |
----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |                |      1 |        |      1 |00:00:00.01 |     282 |    281 |
|   1 |  SORT AGGREGATE               |                |      1 |      1 |      1 |00:00:00.01 |     282 |    281 |
|   2 |   TABLE ACCESS BY INDEX ROWID | SALES          |      1 |   1511 |    318 |00:00:00.01 |     282 |    281 |
|   3 |    BITMAP CONVERSION TO ROWIDS|                |      1 |        |    318 |00:00:00.01 |       3 |      2 |
|*  4 |     BITMAP INDEX SINGLE VALUE | CUST_SALES_BJI |      1 |        |      1 |00:00:00.01 |       3 |      2 |
----------------------------------------------------------------------------------------------------


Outline Data
-------------


  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('11.2.0.1')
      DB_VERSION('11.2.0.1')
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$06E40A32")
      ELIMINATE_JOIN(@"SEL$1" "C"@"SEL$1")
      OUTLINE(@"SEL$1")
      BITMAP_TREE(@"SEL$1" "S"@"SEL$1" AND(("CUSTS"."CUST_CITY")))
      END_OUTLINE_DATA
  */

Predicate Information (identified by operation id):
---------------------------------------------------

   4 - access("S"."SYS_NC00008$"='Berlin')




결합 인덱스
  • 헤더가 잡히는 우선 순위
      • 자주 쓰이는 존재
      • = 조건으로 많이 쓰이는 존재
      • distinct한 값이 더 많은 존재(좁은 범위) = cardinality가 높다


Composite Index(복합 인덱스)
  • index column이 2개 이상인 인덱스


IOT
  •  인덱스로 구성된 테이블
  • overflow segment 가 생성되는 이유는 한 행의 크기가 블럭의 50%를 차지하는 경우 다른 블럭에 나누어 저장
      • 블럭에 더 많은 행을 넣기 위함.
  • ORGANIZATION INDEX TABLESPACE
      • IOT Data가 저장되는 Tablespace
  • PCTTHRESHOLD
      • IOT를 위해서 예약된 공간의 백분율로, 1블럭의 50% 비율보다 큰 데이터가 입력되면 키 열이 아닌 데이터는 OVERFLOW TABLESPACE절에 정의된 테이블스페이스에 저장 됩니다.
      • 0~50 사이값으로만 지정가능, 디폴트는 50
  • INCLUDING
      • IOT 행을 인덱스와 오버플로우 구역으로 나눌 열을 구분합니다.
      •  INCLUDING절이 지정되지 않았는데 행 크기가 PCTTHRESHOLD를 초과하면 기본 키 열을 제외한 모든 이 오버플로우 세그먼트에 저장이 됩니다.
      • INCLUDING절 뒤에 있는 컬럼만 제외하고 모두 오버플로우 세그먼트에 저장이 됩니다.
      • INCLUDING절에 오는 열은 기본 키의 마지막 열의 이름 이거나 기본 키가 아닌 열 입니다.
  • OVERFLOW TABLESPACE
      • PCTTHRESHOLD를 초과하는 Data 행이 저장됩니다.
SYS@orcl2> CREATE TABLE HR.EMP AS
           SELECT EMPLOYEE_ID EMP_ID, last_name, first_name, job_id, hire_date, salary, commission_pct, email, department_id
           FROM HR.EMPLOYEES ;
Table created.


SYS@orcl2> SELECT COUNT(*) FROM HR.EMP;

  COUNT(*)
----------
       107

SYS@orcl2> CREATE INDEX EMP_IDX ON HR.EMP(EMP_ID);
Index created.


SYS@orcl2>  select * from hr.emp where emp_id between 1 and 110;


SYS@orcl2> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
SQL_ID  b5p3q1fc46zb4, child number 0
-------------------------------------
select * from hr.emp where emp_id between 1 and 110

Plan hash value: 1472992808

-------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name    | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |         |      1 |        |     11 |00:00:00.01 |       4 |
|   1 |  TABLE ACCESS BY INDEX ROWID| EMP     |      1 |     11 |     11 |00:00:00.01 |       4 |
|*  2 |   INDEX RANGE SCAN          | EMP_IDX |      1 |     11 |     11 |00:00:00.01 |       2 |
-------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("EMP_ID">=1 AND "EMP_ID"<=110)
Note
-----
   - dynamic sampling used for this statement (level=2)
Elapsed: 00:00:00.06


SYS@orcl2> create table hr.emp_iot
           (emp_id, last_name, first_name, job_id, hire_date, salary, commission_pct, email, department_id, constraint emp_iot_pk primary key(emp_id))
           organization index
           as select * from hr.emp;
Table created.


SYS@orcl2>  execute dbms_stats.gather_table_stats('hr','emp_iot')
PL/SQL procedure successfully completed.


SYS@orcl2> select num_rows, blocks, avg_row_len from DBA_tables where table_name = 'EMP_IOT';

  NUM_ROWS     BLOCKS AVG_ROW_LEN
---------- ---------- -----------
   107                    50


SYS@orcl2> select index_name, blevel, leaf_blocks, clustering_factor from DBA_indexes where table_name = 'EMP_IOT';

INDEX_NAME                         BLEVEL LEAF_BLOCKS CLUSTERING_FACTOR
------------------------------ ---------- ----------- -----------------
EMP_IOT_PK                              0        1                 0


SYS@orcl2> select * from hr.emp_iot where emp_id between 1 and 110;
......


SYS@orcl2>  select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  0urqzs52jby30, child number 0
-------------------------------------
select * from hr.emp_iot where emp_id between 1 and 110

Plan hash value: 2733246072

-----------------------------------------------------------------------------------------
| Id  | Operation        | Name       | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT |            |      1 |        |     11 |00:00:00.01 |       2 |
|*  1 |  INDEX RANGE SCAN| EMP_IOT_PK |      1 |     11 |     11 |00:00:00.01 |       2 |
-----------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("EMP_ID">=1 AND "EMP_ID"<=110)



SYS@orcl2> create table hr.emp_iot_new
           (emp_id, last_name, first_name, job_id, hire_date, salary, commission_pct, email, department_id, constraint emp_iot_new_pk primary key(emp_id))
           organization index tablespace example
           overflow tablespace  users
           including  commission_pct
           as select * from hr.emp;
           
Table created.


SYS@orcl2> execute dbms_stats.gather_table_stats('hr','emp_iot_new')
PL/SQL procedure successfully completed.


SYS@orcl2> select num_rows, blocks, avg_row_len from DBA_tables where table_name = 'EMP_IOT_NEW';

  NUM_ROWS     BLOCKS AVG_ROW_LEN
---------- ---------- -----------
   107                     50



SYS@orcl2> select * from hr.emp_iot_new where emp_id between 1 and 110;
...


SYS@orcl2> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  7y3a6cnzqyjc0, child number 0
-------------------------------------
select * from hr.emp_iot_new where emp_id between 1 and 110

Plan hash value: 2568792030

---------------------------------------------------------------------------------------------
| Id  | Operation        | Name           | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT |                |      1 |        |     11 |00:00:00.01 |       4 |
|*  1 |  INDEX RANGE SCAN| EMP_IOT_NEW_PK |      1 |     11 |     11 |00:00:00.01 |       4 |
---------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("EMP_ID">=1 AND "EMP_ID"<=110)


SYS@orcl2> select emp_id, last_name, first_name, job_id, hire_date, salary, commission_pct from hr.emp_iot_new where emp_id between 1 and 110;
.....


SYS@orcl2> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

SQL_ID  6r6c0pncyhgj2, child number 0
-------------------------------------
select emp_id, last_name, first_name, job_id, hire_date, salary,
commission_pct from hr.emp_iot_new where emp_id between 1 and 110
Plan hash value: 2568792030
----------------------------------------------------
| Id  | Operation        | Name           | E-Rows |
----------------------------------------------------
|   0 | SELECT STATEMENT |                |        |
|*  1 |  INDEX RANGE SCAN| EMP_IOT_NEW_PK |     11 |
----------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("EMP_ID">=1 AND "EMP_ID"<=110)
Note
-----
   - Warning: basic plan statistics not available. These are only collected when:
       * hint 'gather_plan_statistics' is used for the statement or
       * parameter 'statistics_level' is set to 'ALL', at session or system level


SYS@orcl2> select t.table_name as iot, o.table_name as overflow,
       i.index_name, o.tablespace_name as overflow_ts,
       i.tablespace_name as index_ts, i.pct_threshold
from DBA_tables t, DBA_tables o, DBA_indexes i
where  t.table_name = o.iot_name(+)
  and t.table_name = i.table_name
  AND  T.TABLE_NAME IN  ('EMP_IOT','EMP_IOT_NEW');


SYS@orcl2>  select t.table_name as iot, o.table_name as overflow,
            i.index_name, o.tablespace_name as overflow_ts,
            i.tablespace_name as index_ts, i.pct_threshold
            from DBA_tables t, DBA_tables o, DBA_indexes i
            where  t.table_name = o.iot_name(+)
            and t.table_name = i.table_name
            AND  T.TABLE_NAME IN  ('EMP_IOT','EMP_IOT_NEW');

IOT                            OVERFLOW                       INDEX_NAME                     OVERFLOW_TS
------------------------------ ------------------------------ ------------------------------ ------------------------------
INDEX_TS                       PCT_THRESHOLD
------------------------------ -------------
EMP_IOT_NEW                    SYS_IOT_OVER_74906             EMP_IOT_NEW_PK                 USERS
EXAMPLE                                   50

EMP_IOT                                                       EMP_IOT_PK
USERS                                     50



Unusable INDEX
  • Userable의 반댓말은 Valid
1. UNUSABLE INDEX
USER01@orcl2> create table t1 (a number, b char(1000));
Table created.


USER01@orcl2> insert into t1
              select level , dbms_random.string('L',10)
              from dual
              connect by level <=500;
500 rows created.


USER01@orcl2> create index t1_idx on t1(a) ;
Index created.

==========================================================================================
DBMS_RANDOM 패키지내 함수
RANDOM : 랜덤한 숫자 생성
VALUE : 랜덤한 숫자 생성(지정한 범위에서, 디폴트: 0-1)
STRING : 랜덤한 문자열 생성(문자열의 구성은 첫번째 인수값으로 지정.)
첫번째 인수값
U - 대문자
L - 소문자
A - Alphanumeric
X - Alphanumeric (대문자로 된)
P - 출력가능한 문자로만
두번째 인수값 - 문자열길이 지정
==========================================================================================

USER01@orcl2> select * from t1;


USER01@orcl2> select segment_name,extent_id, bytes/1024 kb, blocks
              from user_extents
              where segment_name like 'T1%';

SEGMENT_NAME               EXTENT_ID         KB     BLOCKS
------------------------- ---------- ---------- ----------
T1                                 0         64          8
T1                                 1         64          8
T1                                 2         64          8
T1                                 3         64          8
T1                                 4         64          8
T1                                 5         64          8
T1                                 6         64          8
T1                                 7         64          8
T1                                 8         64          8
T1                                 9         64          8
T1_IDX                             0         64          8
-- extent 10개
-- index 1개


USER01@orcl2> select index_name , status
              from user_indexes
              where table_name = 'T1';


INDEX_NAME                     STATUS
------------------------------ --------
T1_IDX                         VALID


USER01@orcl2> delete from t1
              where mod(a,2)=1;
250 rows deleted.


USER01@orcl2> commit;
Commit complete.


USER01@orcl2> select segment_name, extent_id, bytes/1024 kb, blocks
              from user_extents
              where segment_name ='T1';
              
SEGMENT_NAME               EXTENT_ID         KB     BLOCKS
------------------------- ---------- ---------- ----------
T1                                 0         64          8
T1                                 1         64          8
T1                                 2         64          8
T1                                 3         64          8
T1                                 4         64          8
T1                                 5         64          8
T1                                 6         64          8
T1                                 7         64          8
T1                                 8         64          8
T1                                 9         64          8
-- 반을 지웠는데 왜 extent는 지우기 전과 갯수가 같을까?
-- 이유는 block안의 record는 지워졌지만, HWM는 그대로 있기 떄문.
-- 이럴때 검색 속도를 향상시키기 위해서는 move를 해야한다.


USER01@orcl2> alter table t1 move;
Table altered.


USER01@orcl2> select segment_name,extent_id, bytes/1024 kb, blocks
              from user_extents
              where segment_name like 'T1%';   
              
SEGMENT_NAME               EXTENT_ID         KB     BLOCKS
------------------------- ---------- ---------- ----------
T1                                 0         64          8
T1                                 1         64          8
T1                                 2         64          8
T1                                 3         64          8
T1                                 4         64          8
T1                                 5         64          8
T1_IDX                             0         64          8

7 rows selected.
-- 테이블은 줄어들었고,인덱스 세그먼트 익스텐트 보이지 않는다.
-- unusable 되면 공간을 회수해 준다.
-- 원래는 명단에 T1_IDX가 안보여야 정상...

             
USER01@orcl2> select index_name , status
              from user_indexes
              where table_name = 'T1';

INDEX_NAME                     STATUS
------------------------------ --------
T1_IDX                         UNUSABLE

-- 인덱스 상태만 남겨놓는다


USER01@orcl2> select * from t1 where a=10;  --TAF --> IRS


USER01@orcl2> alter index t1_idx rebuild  nologging;
Index altered.


USER01@orcl2> select index_name , status
              from user_indexes
              where table_name = 'T1';

INDEX_NAME                     STATUS
------------------------------ --------
T1_IDX                         VALID



INVISIBLE INDEX
  • Unusable Index와는 다르게 DML 작업을 하면 Invisible Index들은 계속 유지
  • 옵티마이저에게 invisible 인덱스 사용을 고려하지 않도록 한다.
  • 인덱스를 삭제하기 전에 테스트 하는 용도로도 사용
  • 동일한 컬럼에 다른 유형의 인덱스를 만들어서 테스트하기 위해서도 사용

2. INVISIBLE INDEX
USER01@orcl2> alter index t1_idx  invisible;
Index altered.


USER01@orcl2> set autot on explain


USER01@orcl2> select *
              from t1
              where a=10;


Execution Plan
----------------------------------------------------------
Plan hash value: 3617692013

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |  1015 |    13   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T1   |     1 |  1015 |    13   (0)| 00:00:01 |
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("A"=10)

Note
-----
   - dynamic sampling used for this statement (level=2)


USER01@orcl2> alter index t1_idx visible;
Index altered.


USER01@orcl2> select *
              from t1
              where a=10;

Execution Plan
----------------------------------------------------------
Plan hash value: 546753835

--------------------------------------------------------------------------------------
| Id  | Operation                   | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |        |     1 |  1015 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T1     |     1 |  1015 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | T1_IDX |     1 |       |     1   (0)| 00:00:01 |
--------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("A"=10)
Note
-----
   - dynamic sampling used for this statement (level=2)


가상 컬럼
  • 가상 컬럼은 테이블에있는 하나 이상의 컬럼값을 기반으로
  • 계산식을 적용할 수 있는 11g의 기능이다.
  • 물리적인 공간을 차지 하지 않고 메타데이터로만 저장된다.
  • 파티션 테이블 사용 시 마땅치 않는 컬럼이 없을 경우 가상 컬럼을 통해 적용 가능하다.
  • 가상 컬럼에 인덱스(함수 기반 인덱스) 생성이 가능하며, 통계 정보 수집도 가능하다.
  • 가상 컬럼에는 데이터를 직접 INSERT 할 수 없다.
emp2 sal 값을 통해 사원의 급여 등급 계산하는 가상 열 추가하는 테스트
(1)기존 테이블에 가상 컬럼 추가

USER01@orcl2> alter table emp2 add
              (grade varchar2(1) generated always as (   
               case when sal<= 2000 then 'D'
               when sal > 2000 and sal <= 3000 then 'C'
               when sal > 3000 and sal <= 4000 then 'B'
               else 'A' end) virtual);
Table altered.


(2)테스트
   
USER01@orcl2> select * from emp2;
     EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM     DEPTNO G(가상열)
---------- ---------- --------- ---------- --------- ---------- ---------- ---------- -
      7369 SMITH      CLERK           7902 17-DEC-80        800                    20 D
      7499 ALLEN      SALESMAN        7698 20-FEB-81       1600        300         30 D
      7521 WARD       SALESMAN        7698 22-FEB-81       1250        500         30 D
      7566 JONES      MANAGER         7839 02-APR-81       2975                    20 C
      7654 MARTIN     SALESMAN        7698 28-SEP-81       1250       1400         30 D
      7698 BLAKE      MANAGER         7839 01-MAY-81       2850                    30 C
      7782 CLARK      MANAGER         7839 09-JUN-81       2450                    10 C
      7788 SCOTT      ANALYST         7566 19-APR-87       3000                    20 C
      7839 KING       PRESIDENT            17-NOV-81       5000                    10 A
      7844 TURNER     SALESMAN        7698 08-SEP-81       1500          0         30 D
      7876 ADAMS      CLERK           7788 23-MAY-87       1100                    20 D
      7900 JAMES      CLERK           7698 03-DEC-81        950                    30 D
      7902 FORD       ANALYST         7566 03-DEC-81       3000                    20 C
      7934 MILLER     CLERK           7782 23-JAN-82       1300                    10 D
14 rows selected.


USER01@orcl2> desc emp2  -- grade 보인다.
Name                                                                     Null?    Type
------------------------------------------------------------------------ -------- --------
EMPNO                                                                             NUMBER(4)
ENAME                                                                             VARCHAR2(10)
JOB                                                                               VARCHAR2(9)
MGR                                                                               NUMBER(4)
HIREDATE                                                                          DATE
SAL                                                                               NUMBER(7,2)
COMM                                                                              NUMBER(7,2)
DEPTNO                                                                            NUMBER(2)
GRADE                                                                             VARCHAR2(1)


USER01@orcl2> select empno, sal, grade from emp2;
USER01@orcl2> desc emp2


USER01@orcl2> insert into emp2 (empno , grade)
              values (9000,'A'); --err
insert into emp2 (empno , grade)
            *
ERROR at line 1:
ORA-54013: INSERT operation disallowed on virtual columns
-- 가상열에 추가는 되지 않는다.


USER01@orcl2> insert into emp2 (empno , sal)
              values (9000,5000);
1 row created.


USER01@orcl2> select * from emp2;

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




인덱스는 어떻게 만들고 관리해야하는가? : 지침
  • 대량의 데이터를 삽입할 때는 삽입을 하면서 인덱스를 생성하는게 아닌, 전부 삽입이 된 이후 생성한다.
      • 하지만, 기존의 데이터가 많을 경우, 이 방법은 좋지 않다.
  • 올바른 테이블과 열을 인덱스화
      • UNIQUE하면 할수록 좋다.
      • 조인에 사용되는 열을 인덱스 하면 좋다
      • 작은 테이블은 인덱스 하지 않는다.
  • 인덱스에 적합한 열
      • 넓은 범위의 값( = distinct가 많다) : 일반적인 인덱스
      • 작은 범위의 값( = distinct의 값이 적다) : 비트맵 인덱스
      • 대량의 데이터의 경우 null로 저장
          • 성별 컬럼에서 남자 99% 여자1%일 경우 남자 99%(대량의 데이터)는 null로 되고, 여자 1%가 저장된다.
              • where gender = female이면 'female' 인 값을 빠르게 찾는다
              • where gender = male이면 null인 값을 full scan을 하여 빠르게 찾는다.
  • 인덱스에 적합하지 않은 열
      • 많은 null이 있는데, not null에 대하여 검색하지 않는 경우
      • 가상열