SQL> create table index_org_t (a number primary key, b varchar(10));
* 일반적으로 사용하는 테이블을 '힙 구조 테이블'이라고 부르며, 테이블 생성 시 대개 생략하지만 organization 옵션을 명시할 수도 있다.
SQL> create table heap_org_t (a number primary key, b varchar(10));
비트맵 인덱스에 대한 오해
상이다.
select substr(일자, 1, 6) 월도, sum(판매금액) 총판매금액, avg(판매금액) 평균판매금액
from 영업실적
where 사번 = 's1234'
and 일자 between '20090101' and '20091231'
group by substr(일자, 1, 6)
{code
* 이럴경우 인덱스를 경유해 사원마다 365개 테이블 블록을 읽어야 한다. 클러스터링 팩터가 매우 안 좋기 때문이며, 입력과 조회 패턴이 서로 달라서 생기는 현상이다. 이럴 경우 아래와 같이 사번이 첫 번째 정렬 기준이 되도록 IOT를 구성해 주면, 한 블록만 읽고 처리 할 수 있다.
\\
{code:sql}
Create table 영업실적(사번 varchar2(5), 일자 varchar2(8), ...
Constraint 영업실적_PK primary key (사번, 일자) ) organization index;
select 거래일자,지점번호,계좌번호,sum(거래량),sum(거래금액)
from 일별상품별계좌별거래
where 상품번호 = 'P7006050009'
and 거래일자 between '20080101' and '20080630'
group by 거래일자, 지점번호, 계좌번호
select 거래일자,지점번호,계좌번호,sum(거래량),sum(거래금액)
from 일별상품별계좌별거래
where 거래일자 between '20080101' and '20080630'
group by 거래일자, 지점번호, 계좌번호
Alter index iot_second_idx REBUILD;
Alter index iot_second_idx UPDATE BLOCK REFERENCES;
create table t1(
c1 number not null
, c2 number
, c3 number
, c4 number
, constraint t1_pk primary key (c1) )
organization index ; -- IOT 생성
create index t1_x1 on t1 (c2) ; -- Secondary 인덱스 생성
SQL> insert into t1
2 select rownum, rownum, rownum, rownum
3 from all_objects
4 where rownum <= 1000;
1000 개의 행이 만들어졌습니다.
SQL> commit;
커밋이 완료되었습니다.
SQL> select index_name, PCT_DIRECT_ACCESS
2 from user_indexes
3 where index_name = 'T1_X1';
INDEX_NAME PCT_DIRECT_ACCESS
------------------------------ -----------------
T1_X1 100
SQL> exec dbms_stats.gather_index_stats(user, 't1_x1');
PL/SQL 처리가 정상적으로 완료되었습니다.
SQL> select index_name, PCT_DIRECT_ACCESS
2 from user_indexes
3 where index_name = 'T1_X1';
INDEX_NAME PCT_DIRECT_ACCESS
------------------------------ -----------------
T1_X1 64
SQL> alter index t1_x1 UPDATE BLOCK REFERENCES;
인덱스가 변경되었습니다.
SQL> select index_name, PCT_DIRECT_ACCESS
2 from user_indexes
3 where index_name = 'T1_X1';
INDEX_NAME PCT_DIRECT_ACCESS
------------------------------ -----------------
T1_X1 64
SQL> exec dbms_stats.gather_index_stats(user, 't1_x1');
PL/SQL 처리가 정상적으로 완료되었습니다.
SQL> select index_name, blevel, PCT_DIRECT_ACCESS
2 from user_indexes
3 where index_name = 'T1_X1';
INDEX_NAME BLEVEL PCT_DIRECT_ACCESS
------------------------------ ---------- -----------------
T1_X1 1 100
PCT_DIRECT_ACCESS에 대한 오해
인덱스 통게상 PCT_DIRECT_ACCESS값이 일정 비율 이하로 떨어지면, physical guess로 탐색했다가 PK로 다시 IOT를 스캔하는 비율이 높아져 성능이 떨어진다고 설명한 글이 있는데, 이 값이 100% 미만이면 오라클은 PK를 이용해 바로 IOT를 탐색한다. 오히려 휘발성 IOT에서 이 값이 100%를 가리킬 때가 더 문제 일 수 있다.
exec dbms_stats.set_index_stats (user, 't1_x1', guessq => 100);
T1 테이블에 값을 1부터 1,000까지 차례로 입력했기 때문에 기존 레코드 위치가 바뀔 이유가 없다. 그런데 왜 secondary 인덱스의 pct_direct_access 값이 64로 떨어졌는가?
SQL> create table emp_iot
2 ( empno, ename, job, mgr, hiredate, sal, comm, deptno
3 , constraint pk_emp_iot primary key ( empno ) )
4 organization index
5 as
6 select * from scott.emp;
테이블이 생성되었습니다.
SQL> create index iot_secondary_index on emp_iot( ename );
인덱스가 생성되었습니다.
SQL> set autotrace traceonly explain;
SQL> select /*+ index(emp_iot iot_secondary_index) */ * from emp_iot where ename
= 'SMITH';
Execution Plan
----------------------------------------------------------
------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 87 | 8 (0)|
|* 1 | INDEX UNIQUE SCAN| PK_EMP_IOT | 1 | 87 | 8 (0)|
|* 2 | INDEX RANGE SCAN| IOT_SECONDARY_INDEX | 1 | | 1 (0)|
------------------------------------------------------------------------------
SQL> select /*+ index(emp_iot iot_secondary_index) */ empno from emp_iot where
name = 'SMITH';
Execution Plan
----------------------------------------------------------
-----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
-----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 20 | 1 (0)|
|* 1 | INDEX RANGE SCAN| IOT_SECONDARY_INDEX | 1 | 20 | 1 (0)|
-----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("ENAME"='SMITH')
Note
-----
- 'PLAN_TABLE' is old version
SQL> create cluster c_deptno# ( deptno number(2) ) index ;
클러스터가 생성되었습니다.
SQL> create index i_deptno# on cluster c_deptno#;
인덱스가 생성되었습니다.
SQL> create table emp1
2 cluster c_deptno# (deptno)
3 as
4 select * from scott.emp;
테이블이 생성되었습니다.
SQL> create table dept1
2 cluster c_deptno# (deptno)
3 as
4 select * from scott.dept;
테이블이 생성되었습니다.
SQL> select owner, table_name from dba_tables where cluster_name = 'C_DEPTNO#';
OWNER TABLE_NAME
------------------------------ ------------------------------
SCOTT DEPT1
SCOTT EMP1
SQL> break on deptno skip 1;
SQL> select d.deptno, e.empno, e.ename
2 , dbms_rowid.rowid_block_number(d.rowid) dept_block_no
3 , dbms_rowid.rowid_block_number(e.rowid) emp_block_no
4 from dept1 d, emp1 e
5 where e.deptno = d.deptno
6 order by d.deptno;
DEPTNO EMPNO ENAME DEPT_BLOCK_NO EMP_BLOCK_NO
---------- ---------- ---------- ------------- ------------
10 7839 KING 2352 2352
7782 CLARK 2352 2352
7934 MILLER 2352 2352
20 7902 FORD 2350 2350
7876 ADAMS 2350 2350
7788 SCOTT 2350 2350
7369 SMITH 2350 2350
7566 JONES 2350 2350
30 7900 JAMES 2351 2351
7844 TURNER 2351 2351
7698 BLAKE 2351 2351
7654 MARTIN 2351 2351
7499 ALLEN 2351 2351
7521 WARD 2351 2351
SQL> create cluster objs_cluster# ( object_type VARCHAR2(19) ) index ;
클러스터가 생성되었습니다.
SQL> create index objs_cluster_idx on cluster objs_cluster#;
인덱스가 생성되었습니다.
SQL> create table objs_cluster -- 클러스터 테이블
2 cluster objs_cluster# ( object_type )
3 as
4 select * from all_objects
5 order by dbms_random.value ;
테이블이 생성되었습니다.
SQL> create table objs_regular -- 일반 테이블
2 as
3 select * from objs_cluster
4 order by dbms_random.value;
테이블이 생성되었습니다.
SQL> create index objs_regular_idx on objs_regular(object_type);
인덱스가 생성되었습니다.
SQL> alter table objs_regular modify object_name null;
테이블이 변경되었습니다.
SQL> alter table objs_cluster modify object_name null;
테이블이 변경되었습니다.
select /*+ index(t objs_regular_idx) */ count(object_name)
from objs_regular t
where object_type = 'TABLE';
Rows Row Source Operation
------- ---------------------------------------------------
0 STATEMENT
1 SORT AGGREGATE (cr=622 pr=0 pw=0 time=4153 us)
1632 TABLE ACCESS BY INDEX ROWID OBJS_REGULAR (cr=622 pr=0 pw=0 time=8220 us)
1632 INDEX RANGE SCAN OBJS_REGULAR_IDX (cr=6 pr=0 pw=0 time=1678 us)(Object ID 53035)
-- B*Tree 인덱스와 Heap Table은 테이블을 1.763번 Random 액세스하는 동안 616(=622-6) 개의 블록 I/O가 발생
select count(object_name)
from objs_cluster t
where object_type = 'TABLE';
Rows Row Source Operation
------- ---------------------------------------------------
0 STATEMENT
1 SORT AGGREGATE (cr=21 pr=0 pw=0 time=943 us)
1632 TABLE ACCESS CLUSTER OBJS_CLUSTER (cr=21 pr=0 pw=0 time=1664 us)
1 INDEX UNIQUE SCAN OBJS_CLUSTER_IDX (cr=1 pr=0 pw=0 time=18 us)(Object ID 53031)
-- B*Tree Cluster 인덱스를 통해 클러스터 테이블을 액세스 했더니 Random 액세스는 단 1회만 발생하였고, 클러스터를 스캔하는 동안 20(=21-1)개의 블록 I/O가 발생하였다.
-- 클러스터 인덱스를 '=' 조건으로 액세스할 때는 항상 Unique Scan이 나타난다
Truncate cluster objs_cluster#;
Drop cluster objs_cluster# including tables;
SQL> show parameter block_size;
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
db_block_size integer 8192
SQL> drop cluster c_deptno# including tables;
클러스터가 삭제되었습니다.
SQL> create cluster emp_cluster# ( empno number(4) ) pctfree 0 size 2000 index ;
클러스터가 생성되었습니다.
SQL> create index emp_cluster_idx on cluster emp_cluster#;
인덱스가 생성되었습니다.
SQL> create table emp1
2 cluster emp_cluster# ( empno )
3 as
4 select * from scott.emp;
테이블이 생성되었습니다.
SQL> select emp.empno, emp.ename, dbms_rowid.rowid_block_number(rowid) block_no
2 from emp1 emp;
EMPNO ENAME BLOCK_NO
---------- ---------- ----------
7902 FORD 628
7934 MILLER 628
7369 SMITH 630
7499 ALLEN 630
7521 WARD 630
7566 JONES 630
7654 MARTIN 631
7698 BLAKE 631
7782 CLARK 631
7788 SCOTT 631
7839 KING 632
7844 TURNER 632
7876 ADAMS 632
7900 JAMES 632
SQL> drop table emp1 purge;
테이블이 삭제되었습니다.
SQL> create table emp1
2 cluster emp_cluster# ( empno )
3 as
4 select empno, ename, lpad('*', 970) data -- 한 로우가 1000 바이트쯤 되도록
5 from scott.emp,(select rownum no from dual connect by level <= 10)
6 where empno = 7900;
테이블이 생성되었습니다.
SQL> select empno, ename, dbms_rowid.rowid_block_number(rowid) block_no
2 from emp1 ;
EMPNO ENAME BLOCK_NO
---------- ---------- ----------
7900 JAMES 630
7900 JAMES 630
7900 JAMES 632
7900 JAMES 632
7900 JAMES 632
7900 JAMES 632
7900 JAMES 632
7900 JAMES 632
7900 JAMES 632
7900 JAMES 632
SQL> create cluster username_cluster# ( username varchar2(30) )
2 hashkeys 100 size 50;
클러스터가 생성되었습니다.
SQL> create table user_cluster
2 cluster username_cluster# ( username )
3 as
4 select * from all_users;
테이블이 생성되었습니다.
SQL> create table user_regular as select * from all_users;
테이블이 생성되었습니다.
SQL> create unique index user_regular_idx on user_regular(username);
인덱스가 생성되었습니다.
SQL> alter table user_regular modify user_id null;
테이블이 변경되었습니다.
SQL> alter table user_cluster modify user_id null;
테이블이 변경되었습니다.
SQL> alter session set sql_trace = true;
세션이 변경되었습니다.
SQL> declare
2 l_user_id user_regular.user_id%type;
3 begin
4 for c in (select owner from objs_regular where owner <> 'PUBLIC')
5 loop
6 select user_id into l_user_id from user_regular where username = c.owne
r;
7 select user_id into l_user_id from user_cluster where username = c.owne
r;
8 end loop;
9 end;
10 /
PL/SQL 처리가 정상적으로 완료되었습니다.
SQL> alter session set sql_trace = false;
세션이 변경되었습니다.
SELECT USER_ID
FROM
USER_REGULAR WHERE USERNAME = :B1
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 29856 0.35 0.41 0 0 0 0
Fetch 29856 0.34 0.30 0 59712 0 29856
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 59713 0.70 0.72 0 59712 0 29856
Rows Row Source Operation
------- ---------------------------------------------------
29856 TABLE ACCESS BY INDEX ROWID USER_REGULAR (cr=59712 pr=0 pw=0 time=351640 us)
29856 INDEX UNIQUE SCAN USER_REGULAR_IDX (cr=29856 pr=0 pw=0 time=202659 us)(object id 53043)
SELECT USER_ID
FROM
USER_CLUSTER WHERE USERNAME = :B1
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 29856 0.26 0.33 0 0 0 0
Fetch 29856 0.42 0.25 0 29856 0 29856
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 59713 0.68 0.59 0 29856 0 29856
Rows Row Source Operation
------- ---------------------------------------------------
29856 TABLE ACCESS HASH USER_CLUSTER (cr=29856 pr=0 pw=0 time=225642 us)
- 강좌 URL : http://www.gurubee.net/lecture/3261
- 구루비 강좌는 개인의 학습용으로만 사용 할 수 있으며, 다른 웹 페이지에 게재할 경우에는 출처를 꼭 밝혀 주시면 고맙겠습니다.~^^
- 구루비 강좌는 서비스 제공을 위한 목적이나, 학원 홍보, 수익을 얻기 위한 용도로 사용 할 수 없습니다.