SQL> drop table t ;
Table dropped.
SQL> create table t
2 as
3 select * from all_objects ;
Table created.
SQL> create index t_idx on t ( owner, object_type, object_name );
Index created.
SQL> exec dbms_stats.gather_Table_stats(user,'T',method_opt=>'for all indexed columns');
PL/SQL procedure successfully completed.
SQL> set autotrace traceonly explain
SQL> select owner, object_type
2 from t
3 where owner between 'T' and 'Z'
4 and object_type is not null
5 order by owner DESC, object_type DESC ;
Execution Plan
----------------------------------------------------------
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 333 | 4995 | 1 (0)|
|* 1 | INDEX RANGE SCAN DESCENDING| T_IDX | 333 | 4995 | 1 (0)|
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("OWNER">='T' AND "OWNER"<='Z')
filter("OBJECT_TYPE" IS NOT NULL)
SQL> select owner, object_type
2 from t
3 where owner between 'T' and 'Z'
4 and object_type is not null
5 order by owner DESC, object_type ASC;
Elapsed: 00:00:00.01
Execution Plan
----------------------------------------------------------
----------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
----------------------------------------------------------------
| 0 | SELECT STATEMENT | | 333 | 4995 | 2 (50)|
| 1 | SORT ORDER BY | | 333 | 4995 | 2 (50)|
|* 2 | INDEX RANGE SCAN| T_IDX | 333 | 4995 | 1 (0)|
----------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("OWNER">='T' AND "OWNER"<='Z')
filter("OBJECT_TYPE" IS NOT NULL)
SQL> create index desc_t_idx on t ( owner desc,object_type asc ) ;
Index created.
SQL> select owner, object_type
2 from t
3 where owner between 'T' and 'Z'
4 and object_type is not null
5 order by owner DESC, object_type ASC ;
Elapsed: 00:00:00.01
Execution Plan
----------------------------------------------------------
--------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
--------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 333 | 4995 | 1 (0)|
|* 1 | INDEX RANGE SCAN| DESC_T_IDX | 333 | 4995 | 1 (0)|
--------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access(SYS_OP_DESCEND("OWNER")>=HEXTORAW('A5FF') AND
SYS_OP_DESCEND("OWNER")<=HEXTORAW('ABFF') )
filter(SYS_OP_UNDESCEND(SYS_OP_DESCEND("OWNER"))>='T' AND
SYS_OP_UNDESCEND(SYS_OP_DESCEND("OWNER"))<='Z' AND "OBJECT_TYPE" IS NOT NULL)
h2 인덱스를 사용 하는 두 가지 방법
SQL> set autotrace traceonly explain
SQL> select owner, status
2 from t
3 where owner =user ;
Execution Plan
----------------------------------------------------------
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2283 | 25113 | 1 (0)|
| 1 | TABLE ACCESS BY INDEX ROWID| T | 2283 | 25113 | 1 (0)|
|* 2 | INDEX RANGE SCAN | DESC_T_IDX | 9 | | 1 (0)|
-------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access(SYS_OP_DESCEND("OWNER")=SYS_OP_DESCEND(USER@!))
filter(SYS_OP_UNDESCEND(SYS_OP_DESCEND("OWNER"))=USER@!)
SQL> select count(*)
2 from t
3 where owner =user ;
Execution Plan
----------------------------------------------------------
----------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
----------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 6 | 4 (0)|
| 1 | SORT AGGREGATE | | 1 | 6 | |
|* 2 | INDEX RANGE SCAN| T_IDX | 2283 | 13698 | 4 (0)|
----------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("OWNER"=USER@!)
select * from T where primary_key between :x and :y
SQL> create table colocated ( x int , y varchar2(80));
Table created.
SQL> begin
2 for i in 1 .. 100000
3 loop
4 insert into colocated (x,y) values ( i, rpad(dbms_random.random,75,'*'));
5 end loop;
6 end;
7 /
PL/SQL procedure successfully completed.
SQL>
SQL> alter table colocated add constraint colocated_pk primary key (x) ;
Table altered.
SQL> exec dbms_stats.gather_table_stats(user,'COLOCATED');
PL/SQL procedure successfully completed.
SQL> create table disorganized
2 as
3 select x,y
4 from colocated
5 order by y ;
Table created.
SQL> alter table disorganized add constraint disorganized_pk primary key (x ) ;
Table altered.
SQL> exec dbms_stats.gather_table_stats(user,'DISORGANIZED');
PL/SQL procedure successfully completed.
tkprof nis2010t2_ora_17102.trc tg4.txt sys=no aggregate=no
SQL> select * from colocated where x between 20000 and 40000;
Execution Plan
-------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
---------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 20213 | 1579K| 57 (2)|
| 1 | TABLE ACCESS BY INDEX ROWID| COLOCATED | 20213 | 1579K| 57 (2)|
|* 2 | INDEX RANGE SCAN | COLOCATED_PK | 20213 | | 9 (0)|
---------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("X">=20000 AND "X"<=40000)
select *
from
colocated where x between 20000 and 40000
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 1335 0.10 0.08 0 2899 0 20001
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 1337 0.10 0.08 0 2899 0 20001
SQL> select /*+ index(disorganized disorganized_pk ) */ * from disorganized where x between 20000 and 40000 ;
Execution Plan
------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 19896 | 1554K| 4008(1)|
| 1 | TABLE ACCESS BY INDEX ROWID| DISORGANIZED | 19896 | 1554K| 4008(1)|
|* 2 | INDEX RANGE SCAN | DISORGANIZED_PK | 19896 | | 9(0)|
--------------------------------------------------------------------------------
----
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("X">=20000 AND "X"<=40000)
select /*+ index(disorganized disorganized_pk ) */ *
from
disorganized where x between 20000 and 40000
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 1335 0.20 0.16 0 21354 0 20001
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 1337 0.20 0.17 0 21354 0 20001
[표11-5] 데이터 물리 구조가 인덱스 액세스에 미치는 영향
SQL> select a.index_name, b.num_rows, b.blocks, a.clustering_factor
from user_indexes a, user_tables b
where index_name in ('COLOCATED_PK','DISORGANIZED_PK')
and a.table_name = b.table_name
/
INDEX_NAME NUM_ROWS BLOCKS CLUSTERING_FACTOR
------------------------------ ---------- ---------- -----------------
COLOCATED_PK 101047 1252 1190
DISORGANIZED_PK 99464 1219 99911
논리 I/O에 미치는 ARRARYSIZE 효과
-. ARRAYSIZE는 클라이언트가 다음 로우를 요청할 때 클라이언트에 전송하는 로우의 숫자
-. 클라이언트는 데이터베이스에 다음 로우의 집합을 요청하기 전까지는 이 로우들을 버퍼링 하여 사용
-. 여러 번의 호출로 동일한 블록을 반복해서 읽어 결과를 추출해야 하는 경우라면,
-. 오라클은 그 블록을 버퍼 캐시에서 다시 추출해야 하며, 이때 ARRAYSIZE는 논리 I/O에 주목할 만한 효과를 나타낸다.
-- Organized 테이블에 대하여
SQL> exec dbms_monitor.session_trace_enable
PL/SQL procedure successfully completed.
SQL> show arraysize
arraysize 15
SQL> select * from colocated a15 where x between 20000 and 40000 ;
..,
20001 rows selected.
SQL> set arraysize 100
SQL> show arraysize
arraysize 100
SQL> select * from colocated a100 where x between 20000 and 40000 ;
...
20001 rows selected.
-- Trace 내용
select *
from
colocated a15 where x between 20000 and 40000
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 1335 0.10 0.07 0 2899 0 20001
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 1337 0.10 0.07 0 2899 0 20001
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 59
Rows Row Source Operation
------- ---------------------------------------------------
20001 TABLE ACCESS BY INDEX ROWID COLOCATED (cr=2899 pr=0 pw=0 time=40122 us)
20001 INDEX RANGE SCAN COLOCATED_PK (cr=1374 pr=0 pw=0 time=20097 us)(object id 954366)
STAT #5 id=1 cnt=20001 pid=0 pos=1 obj=954365 op='TABLE ACCESS BY INDEX ROWID COLOCATED (cr=2899 pr=0 pw=0 time=40122 us)'
STAT #5 id=2 cnt=20001 pid=1 pos=1 obj=954366 op='INDEX RANGE SCAN COLOCATED_PK (cr=1374 pr=0 pw=0 time=20097 us)'
********************************************************************************
select *
from
colocated a100 where x between 20000 and 40000
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 202 0.05 0.07 0 684 0 20001
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 204 0.05 0.08 0 684 0 20001
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 59
Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
SQL*Net message to client 202 0.00 0.00
SQL*Net message from client 201 0.11 8.36
SQL*Net more data to client 800 0.00 0.01
STAT #3 id=1 cnt=20001 pid=0 pos=1 obj=954365 op='TABLE ACCESS BY INDEX ROWID COLOCATED (cr=684 pr=0 pw=0 time=40113 us)'
STAT #3 id=2 cnt=20001 pid=1 pos=1 obj=954366 op='INDEX RANGE SCAN COLOCATED_PK (cr=245 pr=0 pw=0 time=20097 us)'
==> ARRAYSIZE 를 15 => 100으로 늘림에 따라서, BLOCK I/O 횟수적으로 줄어든다.
-- Disorganized 테이블에 대하여
SQL> exec dbms_monitor.session_trace_enable
PL/SQL procedure successfully completed.
SQL> show arraysize
arraysize 15
SQL> select /*+ index(a15 disorganized_pk ) */ * from disorganized a15 where x between 20000 and 40000 ;
select /*+ index(a15 disorganized_pk ) */ *
from
disorganized a15 where x between 20000 and 40000
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 1335 0.16 0.16 0 21354 0 20001
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 1337 0.16 0.16 0 21354 0 20001
STAT #4 id=1 cnt=20001 pid=0 pos=1 obj=954407 op='TABLE ACCESS BY INDEX ROWID DISORGANIZED (cr=21354 pr=0 pw=0 time=140154 us)'
STAT #4 id=2 cnt=20001 pid=1 pos=1 obj=954408 op='INDEX RANGE SCAN DISORGANIZED_PK (cr=1374 pr=0 pw=0 time=20169 us)'
SQL> exec dbms_monitor.session_trace_enable
PL/SQL procedure successfully completed.
SQL> set arraysize 100
SQL> show arraysize
arraysize 100
SQL> select /*+ index(a100 disorganized_pk ) */ * from disorganized a100 where x between 20000 and 40000 ;
select /*+ index(a100 disorganized_pk ) */ *
from
disorganized a100 where x between 20000 and 40000
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 202 0.19 0.17 0 20225 0 20001
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 204 0.19 0.17 0 20225 0 20001
STAT #3 id=1 cnt=20001 pid=0 pos=1 obj=954407 op='TABLE ACCESS BY INDEX ROWID DISORGANIZED (cr=20225 pr=0 pw=0 time=140128 us)'
STAT #3 id=2 cnt=20001 pid=1 pos=1 obj=954408 op='INDEX RANGE SCAN DISORGANIZED_PK (cr=245 pr=0 pw=0 time=158 us)'
SQL> select a.index_name, b.num_rows, b.blocks, a.clustering_factor
2 from user_indexes a, user_tables b
3 where index_name in ('COLOCATED_PK','DISORGANIZED_PK')
4 and a.table_name = b.table_name ;
INDEX_NAME NUM_ROWS BLOCKS CLUSTERING_FACTOR
------------------------------ ---------- ---------- -----------------
COLOCATED_PK 101047 1252 1190
DISORGANIZED_PK 99464 1219 99911
select count(y)
from
( select /*+ INDEX(COLOCATED COLOCATED_PK) */ * from COLOCATED )
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 0.30 1.09 1357 1400 0 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 0.30 1.09 1357 1400 0 1
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 59
Rows Row Source Operation
------- ---------------------------------------------------
1 SORT AGGREGATE (cr=1400 pr=1357 pw=0 time=1095288 us)
100000 TABLE ACCESS BY INDEX ROWID COLOCATED (cr=1400 pr=1357 pw=0 time=322568 us)
100000 INDEX FULL SCAN COLOCATED_PK (cr=210 pr=167 pw=0 time=211970 us)(object id 954366)
select count(y)
from
( select /*+ INDEX(DISORGANIZED DISORGANIZED_PK) */ * from DISORGANIZED)
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 0.60 0.93 1401 100121 0 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 0.60 0.94 1401 100121 0 1
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 59
Rows Row Source Operation
------- ---------------------------------------------------
1 SORT AGGREGATE (cr=100121 pr=1401 pw=0 time=939902 us)
100000 TABLE ACCESS BY INDEX ROWID DISORGANIZED (cr=100121 pr=1401 pw=0 time=3045584 us)
100000 INDEX FULL SCAN DISORGANIZED_PK (cr=210 pr=210 pw=0 time=102007 us)(object id 954408)
SQL> set autotrace traceonly explain
SQL> select * from COLOCATED where x between 20000 and 30000 ;
Execution Plan
---------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
---------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 10108 | 789K| 28 (0)|
| 1 | TABLE ACCESS BY INDEX ROWID| COLOCATED | 10108 | 789K| 28 (0)|
|* 2 | INDEX RANGE SCAN | COLOCATED_PK | 10108 | | 4 (0)|
---------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("X">=20000 AND "X"<=30000)
SQL> select * from DISORGANIZED where x between 20000 and 30000 ;
Execution Plan
------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
-----------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 9949 | 777K| 270 (1)|
|* 1 | TABLE ACCESS FULL| DISORGANIZED | 9949 | 777K| 270 (1)|
-----------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("X"<=30000 AND "X">=20000)
모든 인덱스에 좋은 클러스터링 팩터를 가지도록 테이블을 재구성(rebuild)하려고 할 수 있다.
그러나 이것은 대부분이 시간 낭비다.
Why 1. 인덱스 범위 스캔을 통한 넓은 범위의 테이블을 읽는 경우만 효과가 있음 ( Index Range Scan )
2. 일반적으로, 테이블은 클러스터링 팩커가 좋은 단 하나의 인덱스만을 가지지 않는다는 점 ( 대개 Data Migration 시 PK 기준 )