# 요약 | |
Arraysize Allstats Last Partition # Partition Pruning # 정적(Static) 파티션 Pruning # 동적(Dynamic) 파티션 Pruning ■ 로컬 파티션 인덱스 (Local Partitioned Indexes) ■ 파티션 글로벌 인덱스 (Global Partitioned Indexes) |
[ Sample Table : hr.sal_emp ]
HR@ora11g> drop table hr.sal_emp purge;
Table dropped.
HR@ora11g> create table sal_emp
nologging
as
select rownum as employee_id, last_name, first_name, hire_date, job_id, salary, manager_id, department_id
from employees e, (select level as id from dual connect by level <= 5000);
Table created.
HR@ora11g> exec dbms_stats.gather_table_stats('hr','sal_emp')
PL/SQL procedure successfully completed.
HR@ora11g> select num_rows, blocks, avg_row_len from user_tables where table_name = 'SAL_EMP';
NUM_ROWS BLOCKS AVG_ROW_LEN
---------- ---------- -----------
535000 3774 45
# 부분 범위 처리
- SQL에서 주어진 조건을 만족하는 데이터를 전체 범위로 처리하지 않고 운반단위(Arraysize)까지만 먼저 처리하여 그 결과를 유저 프로세스에 전달하고 다음 작업을 계속하겠다는 사용자의 요구가 있을 때까지 잠정적으로 수행을 멈추는 처리방식을 말한다.
- 예를 들어 10000건의 데이터를 스캔해야할 때 1000건만 읽어서 운반단위를 채울 수 있다면 10000건을 한꺼번에 다 읽지 않고 1000개씩 10번으로 나누어서 처리할 수 있다.
> SQL Developer : SQL문 조회 시 50행만 먼저 나오고 스크롤을 내려야 내용이 더 나온다.
환경설정 > 도구 > 데이터베이스 > 고급 > SQL 배열 인출 크기(50 ~ 200) 에서 설정
# 부분범위 처리를 할 수 없는 경우
- 그룹함수를 사용한 경우
- order by 절을 사용한 경우 : 정렬
- union, minus, intersect 를 사용한 경우 : 중복제거 시 sort 수행
# 부분점위 처리를 할 수 없는 경우 대체할 수 있는 방안은?
- order by 절에 사용된 컬럼에 index 를 이용하면 부분범위 처리를 할 수 있다.
- union -> union all + not exists
- minus -> not exists
- intersect -> exists
# 부분 범위 처리결과집합을 전송
- 전체 데이터를 한꺼번에 연속적으로 처리하지 않고 사용자로부터 fetch call이 있을 때마다 일정량 씩 나누어 전송하는 의미
- 오라클은 데이터를 클라이언트에게 전송할 때 일정량 씩 나누어 전송하며 이 설정은 arraysize 파라미터로 설정하여 운반단위를 조절한다.
- 대용량 데이터를 fetch해야할 때 arraysize를 크게 설정하면 이점은?
1. fetch call 횟수가 줄어들어 네트워크 부하가 감소하고 쿼리 성능이 향상된다.
2. 서버프로세스가 읽어야 할 블록 개수 감소
> arraysize : 유저프로세스가 서버프로세스한테 한번에 받을 수 있는 row 수
> 서버프로세스 입장에서 : 1000행을 전달할께
> 유저프로세스 입장에서 : 나는 15행(Arraysize) 밖에 못받으니 서버 니가 조절 좀 해서 주렴
> OLTP(빠른처리)환경말고 풀테이블스캔 대용량, 인덱스 스캔 대용량 등에서 수행
> Arraysize가 너무 크면 데이터가 더 있는줄알고 기다림이 발생할 수 있다.
# Trace Stat + Arraysize 15
HR@ora11g> select * from hr.sal_emp;
535000 rows selected.
HR@ora11g> show arraysize
arraysize 15
> 서버프로세스에서 액티브셋 결과를 15row 만큼 유저프로세스에 전달한다.
# SQL문 통계 정보
HR@ora11g> set autotrace trace stat
HR@ora11g> select * from hr.sal_emp;
535000 rows selected.
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
39134 consistent gets -- 읽은 블록 수
0 physical reads
0 redo size
31341975 bytes sent via SQL*Net to client
392746 bytes received via SQL*Net from client
35668 SQL*Net roundtrips to/from client -- fetch count (fetch call)
0 sorts (memory)
0 sorts (disk)
535000 rows processed -- 읽은 로우 수
> 읽은 로우 수 / fetch count = 535000 / 35668 = 14.999 = arraysize
# Trace Stat + Arraysize 100
# 100row씩 전달하겠다
HR@ora11g> set arraysize 100
HR@ora11g> select * from hr.sal_emp;
535000 rows selected.
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
9019 consistent gets
0 physical reads
0 redo size
27400765 bytes sent via SQL*Net to client
59259 bytes received via SQL*Net from client
5351 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
535000 rows processed
# Trace Stat + Arraysize 1000
# 1000row씩 전달하겠다
HR@ora11g> set arraysize 1000
HR@ora11g> select * from hr.sal_emp;
535000 rows selected.
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
4244 consistent gets
0 physical reads
0 redo size
26774815 bytes sent via SQL*Net to client
6294 bytes received via SQL*Net from client
536 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
535000 rows processed
# Trace Stat + Arraysize 2000
# 2000row씩 전달하겠다
HR@ora11g> set arraysize 2000
HR@ora11g> select * from hr.sal_emp;
535000 rows selected.
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
3980 consistent gets
0 physical reads
0 redo size
26740105 bytes sent via SQL*Net to client
3357 bytes received via SQL*Net from client
269 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
535000 rows processed
# Trace Stat + Arraysize 5000
# 5000row씩 전달하겠다
HR@ora11g> set arraysize 5000
HR@ora11g> select * from hr.sal_emp;
535000 rows selected.
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
3818 consistent gets
0 physical reads
0 redo size
26719175 bytes sent via SQL*Net to client
1586 bytes received via SQL*Net from client
108 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
535000 rows processed
> 1000 이후로 I/O발생에 큰 변화가 없다. 데이터에 맞게 적절하게 조절하자.
HR@ora11g> set autotrace off
HR@ora11g> alter session set statistics_level = all;
Session altered.
# Arraysize 15
HR@ora11g> set arraysize 15
HR@ora11g> select * from sal_emp where salary between 5000 and 8000;
HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads |
------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 125K|00:00:00.05 | 11988 | 4 |
|* 1 | TABLE ACCESS FULL| SAL_EMP | 1 | 128K| 125K|00:00:00.05 | 11988 | 4 |
------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(("SALARY">=5000 AND "SALARY"<=8000))
SYS@ora11g> show parameter db_file_multiblock_read_count
NAME TYPE VALUE
----------------------------- ------- -----
db_file_multiblock_read_count integer 92
> 테이블 블럭 크기 대비 풀스캔했다고 I/O 너무 발생한거 아닌가? > 풀테이블 스캔 시 멀티블럭보다 arraysize의 영향이 크다.
# Arraysize 1000
HR@ora11g> set arraysize 1000
HR@ora11g> select * from sal_emp where salary between 5000 and 8000;
HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
---------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 125K|00:00:00.03 | 3837 |
|* 1 | TABLE ACCESS FULL| SAL_EMP | 1 | 128K| 125K|00:00:00.03 | 3837 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(("SALARY">=5000 AND "SALARY"<=8000))
# Arraysize 2000
HR@ora11g> set arraysize 2000
HR@ora11g> select * from sal_emp where salary between 5000 and 8000;
HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
---------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 125K|00:00:00.03 | 3775 |
|* 1 | TABLE ACCESS FULL| SAL_EMP | 1 | 128K| 125K|00:00:00.03 | 3775 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(("SALARY">=5000 AND "SALARY"<=8000))
> arraysize 1000과 2000의 큰 차이가 없다. 기준은 없으니 적절하게 수정하자.
# Arraysize 5000
HR@ora11g> set arraysize 10000
SP2-0267: arraysize option 10000 out of range (1 through 5000)
> 최대 5000 까지만 설정 가능하다.
HR@ora11g> set arraysize 5000
HR@ora11g> select * from sal_emp where salary between 5000 and 8000;
HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
---------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 125K|00:00:00.03 | 3737 |
|* 1 | TABLE ACCESS FULL| SAL_EMP | 1 | 128K| 125K|00:00:00.03 | 3737 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(("SALARY">=5000 AND "SALARY"<=8000))
** arraysize 대비 데이터 양이 너무 적으면 오히려 데이터가 더 있나 기다림 발생할 수 있다.
# Arraysize 1000 + Index(salary)
HR@ora11g> set arraysize 1000
HR@ora11g> create index hr.sal_emp_idx on hr.sal_emp(salary);
Index created.
--- 대용량이라 강제로 인덱스 스캔 유도 (안하면 Full Scan 나온다)
HR@ora11g> select /*+ index(s sal_emp_idx) */ * from sal_emp s where salary between 5000 and 8000;
HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
--------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads |
--------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 125K|00:00:00.15 | 67143 | 246 |
| 1 | TABLE ACCESS BY INDEX ROWID| SAL_EMP | 1 | 128K| 125K|00:00:00.15 | 67143 | 246 |
|* 2 | INDEX RANGE SCAN | SAL_EMP_IDX | 1 | 128K| 125K|00:00:00.03 | 372 | 246 |
--------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("SALARY">=5000 AND "SALARY"<=8000)
> Random I/O 때문에 Buffer 과다 발생
> Buffers 372 : Buffer Pinning 덕분
> Cluster Factor가 좋으면 상관없지만 대용량에서 Index Scan은 좋을게 없다.
# 테이블 통계 수집, 관련된 인덱스도 통계 수집
HR@ora11g> exec dbms_stats.gather_table_stats('hr', 'sal_emp', cascade => true)
PL/SQL procedure successfully completed.
# 인덱스 통계 수집
HR@ora11g> exec dbms_stats.gather_index_stats('hr', 'sal_emp_idx')
PL/SQL procedure successfully completed.
SYS@ora11g> select num_rows, blocks, avg_row_len, last_analyzed
from user_tables
where table_name = 'SAL_EMP';
NUM_ROWS BLOCKS AVG_ROW_LEN LAST_ANALYZED
---------- ---------- ----------- -------------
535000 3774 45 2024-02-27
HR@ora11g> select index_name, num_rows, blevel, leaf_blocks, clustering_factor, last_analyzed
from user_indexes
where table_name = 'SAL_EMP';
INDEX_NAME NUM_ROWS BLEVEL LEAF_BLOCKS CLUSTERING_FACTOR LAST_ANALYZED
------------------------------ ---------- ---------- ----------- ----------------- -------------
SAL_EMP_IDX 535000 2 1057 218638 2024-02-27
[ Sample Table : hr.sal_emp ]
HR@ora11g> drop table hr.sal_emp;
Table dropped.
HR@ora11g> create table hr.sal_emp
partition by range(salary)
(partition p1 values less than (5000),
partition p2 values less than (10000),
partition p3 values less than (30000),
partition pmax values less than (maxvalue))
nologging
as select rownum as employee_id, last_name, first_name, hire_date, job_id, salary, manager_id, department_id
from hr.employees e, (select level as id from dual connect by level <= 5000);
Table created.
HR@ora11g> exec dbms_stats.gather_table_stats('hr', 'sal_emp', granularity => 'auto')
PL/SQL procedure successfully completed.
HR@ora11g> select partition_name, partition_position, high_value, tablespace_name, num_rows, blocks, avg_row_len
from user_tab_partitions
where table_name = 'SAL_EMP';
PARTITION_NAME PARTITION_POSITION HIGH_VALUE TABLESPACE_NAME NUM_ROWS BLOCKS AVG_ROW_LEN
--------------- ------------------ --------------- --------------- ---------- ---------- -----------
P1 1 5000 USERS 250000 1782 46
P2 2 10000 USERS 195000 1370 45
P3 3 30000 USERS 90000 635 45
PMAX 4 MAXVALUE USERS 0 0 0
# 파티션 정보 포함 실행계획 보기 : Allstats Last Partition
HR@ora11g> select * from sal_emp s where salary between 5000 and 8000;
# 파티션 정보 포함 실행계획 보기 : allstats last partition
HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last partition'));
------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | Pstart| Pstop | A-Rows | A-Time | Buffers |
------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | | | 125K|00:00:00.03 | 1463 |
| 1 | PARTITION RANGE SINGLE| | 1 | 120K| 2 | 2 | 125K|00:00:00.03 | 1463 |
|* 2 | TABLE ACCESS FULL | SAL_EMP | 1 | 120K| 2 | 2 | 125K|00:00:00.02 | 1463 |
------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("SALARY"<=8000)
> Pstart : 파티션 시작 번호 <- PARTITION_POSITION 참고
> Pstop : 파티션 종료 번호
> Partition Pruning 작동
# SQL문 통계 정보
HR@ora11g> show arraysize
arraysize 1000
HR@ora11g> set autotrace trace stat
HR@ora11g> select * from sal_emp s where salary between 5000 and 8000;
125000 rows selected.
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
1463 consistent gets
270 physical reads
0 redo size
4928014 bytes sent via SQL*Net to client
1784 bytes received via SQL*Net from client
126 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
125000 rows processed
HR@ora11g> set autotrace off
# Partition Pruning
- SQL 조건절을 분석하여 읽지 않아도 되는 파티션 세그먼트를 액세스 대상에서 제외시키는 기능
HR@ora11g> alter session set statistics_level = all;
Session altered.
HR@ora11g> set arraysize 1000
# 정적(Static) 파티션 Pruning
- 파티션 키 컬럼을 상수 조건으로 조회
- 액세스할 파티션을 쿼리 최적화 시점에 결정(실행 계획을 생성할 때)
HR@ora11g> select * from sal_emp s where salary between 5000 and 8000;
HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last partition'));
---------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | Pstart| Pstop | A-Rows | A-Time | Buffers | Reads |
---------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | | | 125K|00:00:00.03 | 1463 | 36 |
| 1 | PARTITION RANGE SINGLE| | 1 | 120K| 2 | 2 | 125K|00:00:00.03 | 1463 | 36 |
|* 2 | TABLE ACCESS FULL | SAL_EMP | 1 | 120K| 2 | 2 | 125K|00:00:00.02 | 1463 | 36 |
---------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("SALARY"<=8000)
# 동적(Dynamic) 파티션 Pruning
- 파티션 키 컬럼을 바인드 변수로 조회
- 쿼리의 최적화 시점에 액세스할 파티션을 미리 결정할 수 없다.
실행 시점에 결정된다.(execute)
HR@ora11g> var b_start number
HR@ora11g> var b_stop number
HR@ora11g> execute :b_start := 5000
PL/SQL procedure successfully completed.
HR@ora11g> execute :b_stop := 8000
PL/SQL procedure successfully completed.
HR@ora11g> select * from sal_emp s where salary between :b_start and :b_stop;
HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last partition'));
---------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | Pstart| Pstop | A-Rows | A-Time | Buffers |
---------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | | | 125K|00:00:00.04 | 1463 |
|* 1 | FILTER | | 1 | | | | 125K|00:00:00.04 | 1463 |
| 2 | PARTITION RANGE ITERATOR| | 1 | 120K| KEY | KEY | 125K|00:00:00.03 | 1463 |
|* 3 | TABLE ACCESS FULL | SAL_EMP | 1 | 120K| KEY | KEY | 125K|00:00:00.02 | 1463 |
---------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(:B_STOP>=:B_START)
3 - filter(("SALARY"<=:B_STOP AND "SALARY">=:B_START))
[ Sample Table : hr.emp_non ]
HR@ora11g> create table hr.emp_non
as select rownum as employee_id, last_name, first_name, hire_date, job_id, salary, manager_id, department_id
from hr.employees e, (select level as id from dual connect by level <= 1000);
Table created.
HR@ora11g> exec dbms_stats.gather_table_stats('hr','emp_non')
PL/SQL procedure successfully completed.
SYS@ora11g> select num_rows, blocks, avg_row_len, last_analyzed
from user_tables where table_name = 'EMP_NON';
NUM_ROWS BLOCKS AVG_ROW_LEN LAST_ANALYZED
---------- ---------- ----------- -------------
107000 760 45 2024-02-27
HR@ora11g> alter session set statistics_level = all;
Session altered.
# employee_id = 1000
HR@ora11g> select * from hr.emp_non where employee_id = 1000;
HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
---------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 745 |
|* 1 | TABLE ACCESS FULL| EMP_NON | 1 | 1 | 1 |00:00:00.01 | 745 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("EMPLOYEE_ID"=1000)
# employee_id between 1000 and 25000
HR@ora11g> select count(*) from hr.emp_non where employee_id between 1000 and 25000;
HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
----------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 744 |
| 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.01 | 744 |
|* 2 | TABLE ACCESS FULL| EMP_NON | 1 | 24002 | 24001 |00:00:00.01 | 744 |
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter(("EMPLOYEE_ID"<=25000 AND "EMPLOYEE_ID">=1000))
# index(employee_id) + employee_id = 1000
HR@ora11g> create unique index hr.emp_non_idx on hr.emp_non(employee_id);
Index created.
HR@ora11g> select index_name, num_rows, blevel, leaf_blocks, clustering_factor, last_analyzed
from user_indexes
where table_name = 'EMP_NON';
INDEX_NAME NUM_ROWS BLEVEL LEAF_BLOCKS CLUSTERING_FACTOR LAST_ANALYZED
------------------------------ ---------- ---------- ----------- ----------------- -------------
EMP_NON_IDX 107000 1 223 740 2024-02-27
HR@ora11g> select * from hr.emp_non where employee_id = 1000;
HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
-----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 3 |
| 1 | TABLE ACCESS BY INDEX ROWID| EMP_NON | 1 | 1 | 1 |00:00:00.01 | 3 |
|* 2 | INDEX UNIQUE SCAN | EMP_NON_IDX | 1 | 1 | 1 |00:00:00.01 | 2 |
-----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("EMPLOYEE_ID"=1000)
# index(employee_id) + employee_id between 1000 and 25000
HR@ora11g> select count(*) from hr.emp_non where employee_id between 1000 and 25000;
HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
-------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 51 |
| 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.01 | 51 |
|* 2 | INDEX RANGE SCAN| EMP_NON_IDX | 1 | 24002 | 24001 |00:00:00.01 | 51 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("EMPLOYEE_ID">=1000 AND "EMPLOYEE_ID"<=25000)
>>> 다른 사람은 index fast full scan 나오던데??
# Index Fast Full Scan 유도 : index_ffs()
HR@ora11g> select /*+ index_ffs(e emp_non_idx) */ count(*)
from hr.emp_non e where employee_id between 1000 and 25000;
HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
-----------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 230 |
| 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.01 | 230 |
|* 2 | INDEX FAST FULL SCAN| EMP_NON_IDX | 1 | 24002 | 24001 |00:00:00.01 | 230 |
-----------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter(("EMPLOYEE_ID"<=25000 AND "EMPLOYEE_ID">=1000))
# Index Range Scan 유도 : index_rs()
HR@ora11g> select /*+ index_rs(e emp_non_idx) */ count(*)
from hr.emp_non e where employee_id between 1000 and 25000;
HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
-------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 51 |
| 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.01 | 51 |
|* 2 | INDEX RANGE SCAN| EMP_NON_IDX | 1 | 24002 | 24001 |00:00:00.01 | 51 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("EMPLOYEE_ID">=1000 AND "EMPLOYEE_ID"<=25000)
> INDEX RANGE SCAN인데 성능이 좋은이유? Buffer Pinning 작동 + Clustering Factor가 좋다.
[ Sample Table : hr.emp_local ]
HR@ora11g> create table hr.emp_local
partition by range(employee_id)(
partition p1 values less than (20000),
partition p2 values less than (40000),
partition p3 values less than (80000),
partition p4 values less than (100000),
partition p5 values less than (120000),
partition pmax values less than (maxvalue))
nologging
as select rownum as employee_id, last_name, first_name, hire_date, job_id, salary, manager_id, department_id
from employees e, (select level as id from dual connect by level <= 1000);
Table created.
HR@ora11g> exec dbms_stats.gather_table_stats('hr','emp_local', granularity => 'auto')
PL/SQL procedure successfully completed.
HR@ora11g> select partition_name, partition_position, high_value, tablespace_name, num_rows, blocks, avg_row_len
from user_tab_partitions
where table_name = 'EMP_LOCAL';
PARTITION_NAME PARTITION_POSITION HIGH_VALUE TABLESPACE_NAME NUM_ROWS BLOCKS AVG_ROW_LEN
--------------- ------------------ --------------- --------------- ---------- ---------- -----------
P1 1 20000 USERS 19999 156 44
P2 2 40000 USERS 20000 157 45
P3 3 80000 USERS 40000 294 45
P4 4 100000 USERS 20000 157 45
P5 5 120000 USERS 7001 67 45
PMAX 6 MAXVALUE USERS 0 0 0
SYS@ora11g> select num_rows, blocks, avg_row_len, last_analyzed
from user_tables where table_name = 'EMP_LOCAL';
NUM_ROWS BLOCKS AVG_ROW_LEN LAST_ANALYZED
---------- ---------- ----------- -------------
107000 831 45 2024-02-27
# Range Partition + employee_id = 1000
HR@ora11g> select * from hr.emp_local where employee_id = 1000;
HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last partition'));
--------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | Pstart| Pstop | A-Rows | A-Time | Buffers |
--------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | | | 1 |00:00:00.01 | 140 |
| 1 | PARTITION RANGE SINGLE| | 1 | 1 | 1 | 1 | 1 |00:00:00.01 | 140 |
|* 2 | TABLE ACCESS FULL | EMP_LOCAL | 1 | 1 | 1 | 1 | 1 |00:00:00.01 | 140 |
--------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("EMPLOYEE_ID"=1000)
> 1건 데이터를 찾고자 p1 파티션에 대해 Full Scan
> 파티션으로 나눠서 그나마 I/O가 덜 발생한거다
# Range Partition + employee_id between 1000 and 25000
HR@ora11g> select count(*) from hr.emp_local where employee_id between 1000 and 25000;
HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last partition'));
-----------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | Pstart| Pstop | A-Rows | A-Time | Buffers |
-----------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | | | 1 |00:00:00.01 | 279 |
| 1 | SORT AGGREGATE | | 1 | 1 | | | 1 |00:00:00.01 | 279 |
| 2 | PARTITION RANGE ITERATOR| | 1 | 24002 | 1 | 2 | 24001 |00:00:00.01 | 279 |
|* 3 | TABLE ACCESS FULL | EMP_LOCAL | 2 | 24002 | 1 | 2 | 24001 |00:00:00.01 | 279 |
-----------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter(("EMPLOYEE_ID"<=25000 AND "EMPLOYEE_ID">=1000))
> > 1건 데이터를 찾고자 p1,p2 파티션에 대해 Full Scan
/*
index = segment
index = sort된 결과, 수정이 잦으면 안됨
- Index의 pctfree는 Index의 Split 현상 방지
- Table의 pctfree는 Row Migration 현상 방지
*/
■ 로컬 파티션 인덱스
- 파티션 테이블의 각 파티션과 파티션 인덱스의 각 파티션이 1 대 1 로 맵핑이 되는 인덱스이다.
- 테이블 파티션의 갯수화 인덱스 파티션의 갯수가 일치하며 파티션 테이블의 파티션 키와 파티션 인덱스의 인덱스 키가 일치한다.
- 유니관리 작업은 오라클이 자동으로 관리한다. 다른 파티션에 영량을 주지 않는다.
- prefixed : 파티션 인덱스를 생성할 때 파티션 키 컬럼을 인덱스 키 컬럼 왼쪽 선두에 두는 것을 의미
- nonprefixed : 파티션 인덱스를 생성할 때 파티션 키 컬럼을 인덱스 키 컬럼 왼쪽 선두에 두지 않는 것을 의미
# 파티션 키컬럼(employee_id)에 대해 Index 설정 : local
HR@ora11g> create unique index hr.emp_local_idx on hr.emp_local(employee_id) local;
Index created.
HR@ora11g> select i.index_name, i.uniqueness, p.locality, p.alignment, i.partitioned, p.partition_count
from user_indexes i, user_part_indexes p
where i.table_name = 'EMP_LOCAL'
and p.table_name = i.table_name
and p.index_name = i.index_name;
INDEX_NAME UNIQUENESS LOCALITY ALIGNMENT PARTITIONED PARTITION_COUNT
------------------------------ ---------- ---------- ------------ ----------- ---------------
EMP_LOCAL_IDX UNIQUE LOCAL PREFIXED YES 6
HR@ora11g> select index_name, partition_name from user_ind_partitions where index_name = 'EMP_LOCAL_IDX';
INDEX_NAME PARTITION_NAME
------------------------------ ---------------
EMP_LOCAL_IDX P1
EMP_LOCAL_IDX P2
EMP_LOCAL_IDX P3
EMP_LOCAL_IDX P4
EMP_LOCAL_IDX P5
EMP_LOCAL_IDX PMAX
# Local + employee_id = 1000
HR@ora11g> select * from hr.emp_local where employee_id = 1000;
HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last partition'));
------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | Pstart| Pstop | A-Rows | A-Time | Buffers |
------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | | | 1 |00:00:00.01 | 3 |
| 1 | PARTITION RANGE SINGLE | | 1 | 1 | 1 | 1 | 1 |00:00:00.01 | 3 |
| 2 | TABLE ACCESS BY LOCAL INDEX ROWID| EMP_LOCAL | 1 | 1 | 1 | 1 | 1 |00:00:00.01 | 3 |
|* 3 | INDEX UNIQUE SCAN | EMP_LOCAL_IDX | 1 | 1 | 1 | 1 | 1 |00:00:00.01 | 2 |
------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("EMPLOYEE_ID"=1000)
# Local + employee_id between 1000 and 25000
HR@ora11g> select count(*) from hr.emp_local where employee_id between 1000 and 25000;
HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last partition'));
---------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | Pstart| Pstop | A-Rows | A-Time | Buffers |
---------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | | | 1 |00:00:00.01 | 53 |
| 1 | SORT AGGREGATE | | 1 | 1 | | | 1 |00:00:00.01 | 53 |
| 2 | PARTITION RANGE ITERATOR| | 1 | 24002 | 1 | 2 | 24001 |00:00:00.01 | 53 |
|* 3 | INDEX RANGE SCAN | EMP_LOCAL_IDX | 2 | 24002 | 1 | 2 | 24001 |00:00:00.01 | 53 |
---------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("EMPLOYEE_ID">=1000 AND "EMPLOYEE_ID"<=25000)
>>> 다른사람은 table access full scan 나오는데??? > INDEX RANGE SCAN 이 훨씬낫다.
[ Sample Table : hr.emp_global]
HR@ora11g> create table hr.emp_global
partition by range(employee_id)(
partition p1 values less than (20000),
partition p2 values less than (40000),
partition p3 values less than (80000),
partition p4 values less than (100000),
partition p5 values less than (120000),
partition pmax values less than (maxvalue))
as select rownum as employee_id, last_name, first_name, hire_date, job_id, salary, manager_id, department_id
from employees e, (select level as id from dual connect by level <= 1000);
Table created.
HR@ora11g> exec dbms_stats.gather_table_stats('hr','emp_global', granularity => 'auto')
PL/SQL procedure successfully completed.
HR@ora11g> select partition_name, partition_position, high_value, tablespace_name, num_rows, blocks, avg_row_len
from user_tab_partitions
where table_name = 'EMP_GLOBAL';
PARTITION_NAME PARTITION_POSITION HIGH_VALUE TABLESPACE_NAME NUM_ROWS BLOCKS AVG_ROW_LEN
--------------- ------------------ --------------- --------------- ---------- ---------- -----------
P1 1 20000 USERS 19999 156 44
P2 2 40000 USERS 20000 157 45
P3 3 80000 USERS 40000 296 45
P4 4 100000 USERS 20000 157 45
P5 5 120000 USERS 7001 67 45
PMAX 6 MAXVALUE USERS 0 0 0
■ 파티션 글로벌 인덱스 (Partition Global Index)
- 파티션 테이블의 파티션 개수와 인덱스 파티션 의 파티션 개수가 일치 하지 않는다.
- 파티션 테이블의 파티션 키와 인덱스 파티션 키가 일치하지 않는다.
- 유지관리는 사용자(DBA)가 직접 해야한다.
# Global Index(hire_date) 생성
--- 사원번호 보다 날짜 검색이 많다는 가정에 인덱스 설정
HR@ora11g> create index hr.emp_global_idx on hr.emp_global(hire_date) global
partition by range(hire_date)(
partition p2004 values less than (to_date('2005-01-01','yyyy-mm-dd')),
partition p2005 values less than (to_date('2006-01-01','yyyy-mm-dd')),
partition p2006 values less than (to_date('2007-01-01','yyyy-mm-dd')),
partition pmax values less than (maxvalue));
Index created.
HR@ora11g> select i.index_name, i.uniqueness, p.locality, p.alignment, i.partitioned, p.partition_count
from user_indexes i, user_part_indexes p
where i.table_name = 'EMP_GLOBAL'
and p.table_name = i.table_name
and p.index_name = i.index_name;
INDEX_NAME UNIQUENESS LOCALITY ALIGNMENT PARTITIONED PARTITION_COUNT
------------------------------ ---------- ---------- ------------ ----------- ---------------
EMP_GLOBAL_IDX NONUNIQUE GLOBAL PREFIXED YES 4
HR@ora11g> select index_name, partition_name, high_value , blevel, leaf_blocks, num_rows, clustering_factor, last_analyzed, pct_free, status
from user_ind_partitions
where index_name = 'EMP_GLOBAL_IDX';
INDEX_NAME PARTITION_NAME HIGH_VALUE BLEVEL LEAF_BLOCKS NUM_ROWS CLUSTERING_FACTOR LAST_ANALYZED PCT_FREE STATUS
--------------- --------------- --------------- ----------------------------------------------------------------- ------ ----------- -------- ----------------- ------------- -------- ------
EMP_GLOBAL_IDX P2004 TO_DATE(' 2005-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA') 1 77 24000 15549 2024-02-27 10 USABLE
EMP_GLOBAL_IDX P2005 TO_DATE(' 2006-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA') 1 93 29000 19999 2024-02-27 10 USABLE
EMP_GLOBAL_IDX P2006 TO_DATE(' 2007-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA') 1 77 24000 17040 2024-02-27 10 USABLE
EMP_GLOBAL_IDX PMAX MAXVALUE 1 97 30000 20000 2024-02-27 10 USABLE
HR@ora11g> select count(*) from hr.emp_global
where hire_date between to_date('2001-01-01','yyyy-mm-dd') and to_date('2001-12-31','yyyy-mm-dd');
HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last partition'));
--------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | Pstart| Pstop | A-Rows | A-Time | Buffers |
--------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | | | 1 |00:00:00.01 | 85 |
| 1 | SORT AGGREGATE | | 1 | 1 | | | 1 |00:00:00.01 | 85 |
| 2 | PARTITION RANGE SINGLE| | 1 | 15278 | 1 | 1 | 1000 |00:00:00.01 | 85 |
|* 3 | INDEX FAST FULL SCAN | EMP_GLOBAL_IDX | 1 | 15278 | 1 | 1 | 1000 |00:00:00.01 | 85 |
--------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter(("HIRE_DATE"<=TO_DATE(' 2001-12-31 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND
"HIRE_DATE">=TO_DATE(' 2001-01-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss')))
> INDEX FAST FULL SCAN > 인덱스만으로도 카운트 > not null 제약조건이 있기 때문에 가능
HR@ora11g> desc hr.emp_global
Name Null Type
------------- -------- ------------
EMPLOYEE_ID NUMBER
LAST_NAME NOT NULL VARCHAR2(25)
FIRST_NAME VARCHAR2(20)
HIRE_DATE NOT NULL DATE v
JOB_ID NOT NULL VARCHAR2(10)
SALARY NUMBER(8,2)
MANAGER_ID NUMBER(6)
DEPARTMENT_ID NUMBER(4)
# INDEX RANGE SCAN
HR@ora11g> select /*+ index_rs(e emp_global_idx) */ count(*) from hr.emp_global e
where hire_date between to_date('2001-01-01','yyyy-mm-dd') and to_date('2001-12-31','yyyy-mm-dd');
HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last partition'));
--------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | Pstart| Pstop | A-Rows | A-Time | Buffers |
--------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | | | 1 |00:00:00.01 | 5 |
| 1 | SORT AGGREGATE | | 1 | 1 | | | 1 |00:00:00.01 | 5 |
| 2 | PARTITION RANGE SINGLE| | 1 | 15278 | 1 | 1 | 1000 |00:00:00.01 | 5 |
|* 3 | INDEX RANGE SCAN | EMP_GLOBAL_IDX | 1 | 15278 | 1 | 1 | 1000 |00:00:00.01 | 5 |
--------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("HIRE_DATE">=TO_DATE(' 2001-01-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND
"HIRE_DATE"<=TO_DATE(' 2001-12-31 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))
> 파티션 인덱스를 범위스캔 시 버퍼피닝이 발생하여 더 효율적
# 파티션 부분 삭제
HR@ora11g> select partition_name, partition_position, high_value, tablespace_name, num_rows, blocks, avg_row_len
from user_tab_partitions
where table_name = 'EMP_GLOBAL';
PARTITION_NAME PARTITION_POSITION HIGH_VALUE TABLESPACE_NAME NUM_ROWS BLOCKS AVG_ROW_LEN
--------------- ------------------ -------------- --------------- ---------- ---------- -----------
P1 1 20000 USERS 19999 156 44
P2 2 40000 USERS 20000 157 45
P3 3 80000 USERS 40000 296 45
P4 4 100000 USERS 20000 157 45
P5 5 120000 USERS 7001 67 45
PMAX 6 MAXVALUE USERS 0 0 0
HR@ora11g> select index_name, partition_name ,high_value, blevel, leaf_blocks, num_rows, clustering_factor, last_analyzed, pct_free, status
from user_ind_partitions
where index_name = 'EMP_GLOBAL_IDX';
INDEX_NAME PARTITION_NAME HIGH_VALUE BLEVEL LEAF_BLOCKS NUM_ROWS CLUSTERING_FACTOR LAST_ANALYZED PCT_FREE STATUS
--------------- --------------- --------------- ------------------------------------------------------------------ ------ ----------- -------- ----------------- ------------- -------- ------
EMP_GLOBAL_IDX P2004 TO_DATE(' 2005-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA') 1 77 24000 15549 2024-02-27 10 USABLE
EMP_GLOBAL_IDX P2005 TO_DATE(' 2006-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA') 1 93 29000 19999 2024-02-27 10 USABLE
EMP_GLOBAL_IDX P2006 TO_DATE(' 2007-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA') 1 77 24000 17040 2024-02-27 10 USABLE
EMP_GLOBAL_IDX PMAX MAXVALUE 1 97 30000 20000 2024-02-27 10 USABLE
--- 파티션 부분 삭제 : 데이터도 함께 삭제되는거 맞다.
HR@ora11g> alter table hr.emp_global drop partition p4;
Table altered.
HR@ora11g> select /*+ index_rs(e emp_global_idx) */ count(*) from hr.emp_global e
where hire_date between to_date('2001-01-01','yyyy-mm-dd') and to_date('2001-12-31','yyyy-mm-dd');
ORA-01502: index 'HR.EMP_GLOBAL_IDX' or partition of such index is in unusable state
HR@ora11g> select index_name, partition_name ,high_value, blevel, leaf_blocks, num_rows, clustering_factor, last_analyzed, pct_free, status
from user_ind_partitions
where index_name = 'EMP_GLOBAL_IDX';
INDEX_NAME PARTITION_NAME HIGH_VALUE BLEVEL LEAF_BLOCKS NUM_ROWS CLUSTERING_FACTOR LAST_ANALYZED PCT_FREE STATUS
--------------- --------------- --------------- ------------------------------------------------------------------ ------ ----------- -------- ----------------- ------------- -------- --------
EMP_GLOBAL_IDX P2004 TO_DATE(' 2005-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA') 1 77 24000 15549 2024-02-27 10 UNUSABLE
EMP_GLOBAL_IDX P2005 TO_DATE(' 2006-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA') 1 93 29000 19999 2024-02-27 10 UNUSABLE
EMP_GLOBAL_IDX P2006 TO_DATE(' 2007-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA') 1 77 24000 17040 2024-02-27 10 UNUSABLE
EMP_GLOBAL_IDX PMAX MAXVALUE 1 97 30000 20000 2024-02-27 10 UNUSABLE
> 파티션을 부분 삭제한거지 인덱스는 그대로 있다
> 단, STATUS : USABLE -> UNUSABLE
--- Index Rebuild
HR@ora11g> alter index hr.emp_global_idx rebuild;
ORA-14086: a partitioned index may not be rebuilt as a whole
> 파티션 글로벌 인덱스는 각각 리빌드 해야한다. > 리터럴 문자 만들어서 일괄로 작업
↓
HR@ora11g> alter index hr.emp_global_idx rebuild partition p2004;
Index altered.
HR@ora11g> alter index hr.emp_global_idx rebuild partition p2005;
Index altered.
HR@ora11g> alter index hr.emp_global_idx rebuild partition p2006;
Index altered.
HR@ora11g> alter index hr.emp_global_idx rebuild partition pmax;
Index altered.
HR@ora11g> select index_name, partition_name, high_value , blevel, leaf_blocks, num_rows, clustering_factor, last_analyzed, pct_free, status
from user_ind_partitions
where index_name = 'EMP_GLOBAL_IDX';
INDEX_NAME PARTITION_NAME HIGH_VALUE BLEVEL LEAF_BLOCKS NUM_ROWS CLUSTERING_FACTOR LAST_ANALYZED PCT_FREE STATUS
--------------- --------------- --------------- ------------------------------------------------------------------ ------ ----------- -------- ----------------- ------------- -------- ------
EMP_GLOBAL_IDX P2004 TO_DATE(' 2005-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA') 1 63 19515 12634 2024-02-27 10 USABLE
EMP_GLOBAL_IDX P2005 TO_DATE(' 2006-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA') 1 76 23580 16250 2024-02-27 10 USABLE
EMP_GLOBAL_IDX P2006 TO_DATE(' 2007-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA') 1 63 19514 13847 2024-02-27 10 USABLE
EMP_GLOBAL_IDX PMAX MAXVALUE 1 79 24391 16255 2024-02-27 10 USABLE
> STATUS : UNUSABLE -> USABLE
HR@ora11g> select /*+ index_rs(e emp_global_idx) */ count(*) from hr.emp_global e
where hire_date between to_date('2001-01-01','yyyy-mm-dd') and to_date('2001-12-31','yyyy-mm-dd');
HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last partition'));
--------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | Pstart| Pstop | A-Rows | A-Time | Buffers |
--------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | | | 1 |00:00:00.01 | 4 |
| 1 | SORT AGGREGATE | | 1 | 1 | | | 1 |00:00:00.01 | 4 |
| 2 | PARTITION RANGE SINGLE| | 1 | 15278 | 1 | 1 | 813 |00:00:00.01 | 4 |
|* 3 | INDEX RANGE SCAN | EMP_GLOBAL_IDX | 1 | 15278 | 1 | 1 | 813 |00:00:00.01 | 4 |
--------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("HIRE_DATE">=TO_DATE(' 2001-01-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND
"HIRE_DATE"<=TO_DATE(' 2001-12-31 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))
> Rebuild 덕분에 I/O 1개 줄었다!
# Global Index 사용 중 파티션 추가 : Add X -> Split
alter index hr.emp_global_idx add partition p2007 values less than (to_date('2008-01-01','yyyy-mm-dd');
hash 말고는 add partition 추가 불가 -> split
alter index hr.emp_global_idx split partition pmax at (to_date('2008-01-01',' yyyy-mm-dd')) into (partition p2007, partition pmax);
HR@ora11g> select index_name, partition_name, high_value , blevel, leaf_blocks, num_rows, clustering_factor, last_analyzed, pct_free, status
from user_ind_partitions
where index_name = 'EMP_GLOBAL_IDX';
alter index emp_global_ind drop partition pmax;
>
alter index hr.emp_global_idx rebuild partition pmax;
HR@ora11g> select index_name, partition_name, high_value , blevel, leaf_blocks, num_rows, clustering_factor, last_analyzed, pct_free, status
from user_ind_partitions
where index_name = 'EMP_GLOBAL_IDX';
'Oracle 11g > Tuning' 카테고리의 다른 글
95-1 DML작업 병렬처리 (0) | 2024.02.29 |
---|---|
94 Parallel (3) | 2024.02.28 |
92 Partition (1) | 2024.02.26 |
91 통계정보 (0) | 2024.02.23 |
90 Pushdown, Pullup, Use_concat, Optimazer (0) | 2024.02.22 |