h1.통계정보 관련 새로운 기능
11g R2에서 소개되었으며, dbms_stats 패키지를 사용해서 수집
12c New Feature : 컬럼 그룹 통계가 이점이 있을 것 같은 컬럼의 조합을 감지하고 이를 자동으로 컬럼 그룹 통계로 만드는 작업을 수행. SQL Plan Directives를 통해 이루어 진다.
DBMS_STATS.SEED_COL_USAGE ( sqlset_name IN VARCHAR2, owner_name IN VARCHAR2, time_limit IN POSITIVE DEFAULT NULL);
SQL> exec dbms_stats.seed_col_usage(null,'SCOTT',1800);
* 컬럼 사용 통계 정보 수집
* 특정 기간 동안 현재 부하를 모니터링해서 생성
* 컬럼 그룹 통계를 생성할 수 있는 특정 컬럼을 제안
* 백그라운드 작업 수행
-- 위의 seed_col_usage를 수행 후, 아래 SQL 문장 수행
SQL> select mgr, job, count(*)
2 from emp
3 where job='CLERK' and mgr=7902 and hiredate < sysdate
4 group by mgr, job;
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 196K| 3840K| 4693 (1)| 00:00:01 |
| 1 | SORT GROUP BY NOSORT | | 196K| 3840K| 4693 (1)| 00:00:01 |
|* 2 | TABLE ACCESS FULL | COPY_EMP | 196K| 3840K| 4693 (1)| 00:00:01 |
----------------------------------------------------------------------------------------
SQL> select dbms_stats.report_col_usage('GHLEE','EMP') from dual;
###############################################################################
COLUMN USAGE REPORT FOR GHLEE.EMP
................................. --> 컬럼 조건 상태 확인
1. HIREDATE : RANGE
2. JOB : EQ
3. MGR : EQ
DBMS_STATS.REPORT_COL_USAGE('GHLEE','EMP')
--------------------------------------------------------------------------------
4. (JOB, MGR) : FILTER --> 컬럼 그룹 통계 제안
###############################################################################
SQL> SELECT DBMS_STATS.CREATE_EXTENDED_STATS('GHLEE', 'EMP') FROM DUAL
DBMS_STATS.CREATE_EXTENDED_STATS('GHLEE','EMP')
--------------------------------------------------------------------------------
###############################################################################
EXTENSIONS FOR GHLEE.EMP
........................
1. (JOB, MGR) : SYS_STULPA1A#B6YL4KQ59DQO3OADQ created
###############################################################################
-- 확장 통계 생성 후 통계 정보 재 수집
SQL> EXEC DBMS_STATS.GATHER_TABLE_STATS('GHLEE', 'EMP', CASCADE=>TRUE);
SQL> select column_name, num_distinct, histogram
2 from user_tab_col_statistics
3 where table_name = 'EMP'
4 order by 1;
COLUMN_NAME NUM_DISTINCT HISTOGRAM
------------------------------ ------------ ---------------
COMM 3 NONE
DEPTNO 3 NONE
EMPNO 14 NONE
ENAME 14 NONE
HIREDATE 13 FREQUENCY
JOB 5 FREQUENCY
MGR 6 FREQUENCY
SAL 12 NONE
SYS_STULPA1A#B6YL4KQ59DQO3OADQ 8 FREQUENCY
SQL> insert into emp
2 select rownum no, ename, job, mgr, hiredate, sal, comm, deptno
3 from (select * from emp),
4 (select rownum from dual connect by level <= 200000);
SQL> exec dbms_stats.gather_table_stats('GHLEE', 'EMP', CASCADE=>TRUE);
SQL> select column_name, num_distinct, histogram
2 from user_tab_col_statistics
3 where table_name = 'EMP'
4 order by 1;
COLUMN_NAME NUM_DISTINCT HISTOGRAM
------------------------------ ------------ ---------------
COMM 3 NONE
DEPTNO 3 NONE
EMPNO 2800014 NONE
ENAME 14 NONE
HIREDATE 13 FREQUENCY
JOB 5 FREQUENCY
MGR 6 FREQUENCY
SAL 12 NONE
SYS_STULPA1A#B6YL4KQ59DQO3OADQ 8 FREQUENCY
SQL> select mgr, job, count(*)
2 from emp
3 where job='CLERK' and mgr=7902 and hiredate < sysdate
4 group by mgr, job;
-----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 200K| 3906K| 4708 (1)| 00:00:01 |
| 1 | SORT GROUP BY NOSORT| | 200K| 3906K| 4708 (1)| 00:00:01 |
|* 2 | TABLE ACCESS FULL | EMP | 200K| 3906K| 4708 (1)| 00:00:01 |
-----------------------------------------------------------------------------
-- MGR과 JOB 컬럼 값의 선택도가 높지 않기 때문에 FULL TABLE SCAN을 이용
SQL> update emp
2 set mgr=empno;
SQL> exec dbms_stats.gather_table_stats('GHLEE', 'EMP', CASCADE=>TRUE);
SQL> select column_name, num_distinct, histogram
2 from user_tab_col_statistics
3 where table_name = 'EMP'
4 order by 1;
COLUMN_NAME NUM_DISTINCT HISTOGRAM
------------------------------ ------------ ---------------
COMM 3 NONE
DEPTNO 3 NONE
EMPNO 2800014 NONE
ENAME 14 NONE
HIREDATE 13 FREQUENCY
JOB 5 FREQUENCY
MGR 2800014 NONE
SAL 12 NONE
SYS_STULPA1A#B6YL4KQ59DQO3OADQ 2776064 NONE
SQL> select mgr, job, count(*)
2 from emp
3 where job='CLERK' and mgr=7902 and hiredate < sysdate
4 group by mgr, job;
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 22 | 4 (0)| 00:00:01 |
| 1 | SORT GROUP BY NOSORT | | 1 | 22 | 4 (0)| 00:00:01 |
|* 2 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 22 | 4 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | EMP_IDX1 | 1 | | 3 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
SQL> show parameter resource
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
resource_limit boolean TRUE
resource_manager_cpu_allocation integer 64
resource_manager_plan string
SQL> alter system set resource_manager_plan = 'DEFAULT_PLAN';
SQL> show parameter job_queue
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
job_queue_processes integer 1000
SQL> exec dbms_stats.set_global_prefs('CONCURRENT', 'ALL');
SQL> SELECT DBMS_STATS.GET_PREFS('CONCURRENT') FROM DUAL;
DBMS_STATS.GET_PREFS('CONCURRENT')
--------------------------------------------------------------------------------
ALL
-- 해당 스키마 각각의 오브젝트(테이블, 파티션, 인덱스, 기타)별로 Job을 생성한다. 이런 통계 작업들은 병렬로 각각의 작업을 수행 할 수 있다.
SQL> exec dbms_stats.gather_schema_stats('GHLEE', CASCADE => TRUE);
SQL> SELECT TARGET, TARGET_TYPE, JOB_NAME, TO_CHAR(START_TIME, 'DD-MON-YYYY HH24:MI:SS') DAY
2 FROM DBA_OPTSTAT_OPERATION_TASKS
3 WHERE STATUS = 'COMPLETED'
4 AND OPID = (SELECT MAX(ID)
5 FROM DBA_OPTSTAT_OPERATIONS
6 WHERE OPERATION = 'gather_schema_stats')
TARGET TARGET_TYPE JOB_NAME DAY
------------------------------------------------------------ -------------------- ---------- ------------------------------
GHLEE.RTE_BACKUP_INX.RTE_BACKUP_20151125 INDEX PARTITION 13-JAN-2017 10:49:35
GHLEE.RTE_BACKUP_INX.RTE_BACKUP_20151126 INDEX PARTITION 13-JAN-2017 10:49:35
GHLEE.RTE_BACKUP_INX.RTE_BACKUP_20151127 INDEX PARTITION 13-JAN-2017 10:49:35
GHLEE.RTE_BACKUP_INX.RTE_BACKUP_20151128 INDEX PARTITION 13-JAN-2017 10:49:35
GHLEE.RTE_BACKUP_INX.RTE_BACKUP_20151129 INDEX PARTITION 13-JAN-2017 10:49:35
GHLEE.RTE_BACKUP_INX.RTE_BACKUP_20151130 INDEX PARTITION 13-JAN-2017 10:49:35
GHLEE.RTE_BACKUP_INX.RTE_BACKUP_20151201 INDEX PARTITION 13-JAN-2017 10:49:35
GHLEE.RTE_BACKUP_INX.RTE_BACKUP_20151202 INDEX PARTITION 13-JAN-2017 10:49:35
GHLEE.RTE_BACKUP_INX.RTE_BACKUP_20151203 INDEX PARTITION 13-JAN-2017 10:49:35
GHLEE.RTE_BACKUP_INX.RTE_BACKUP_20151204 INDEX PARTITION 13-JAN-2017 10:49:35
톰 카이트의 한마디
동시 통계를 사용하기 전에 이 작업이 적절한지 먼저 확인해야 함.
서버의 리소스를 가급적이면 많이 사용하기 위해서 단일 작업으로 디자인 되었다. 모든 머신의 리소스를 소모하는 단일 작업을 위해 고려해야 하는 것은 이 작업 이외의 어떤 작업도 수행하지 않아야 한다는 사실이다. DW와 같은 환경에서 통계 정보를 구동하는 것이 아니라면 높은 레벨의 동시 통계 정보 수집은 주의해야 한다.
또한, JOB_QUEUE_PROCESS 파라미터의 디폴트 값이 1000이라는 사실이다. 만약 많은 개수의 저용량 디스크를 보유하고 있거나 500개 이상의 CPU 개수를 가진 머신이 아니라면
1000이라는 수치는 너무 높을 수 있다. 이 설정 값으로 적절한 값이 사용되는지를 반드시 확인해야 한다.(물리적인 CPU 개수의 2~4배 정도록 시작하는 것이 좋다)
Synopsis
파티션의 컬럼과 파티션을 위한 통계 메타 데이터를 시놉시스라고 한다.
증분 글로벌 통계는 테이블의 각 파티션마다 시놉시스를 생성하고 이를 저장해서 이용한다.
각 시놉시스는 SYSAUX 테이블스페이스에 10KB 크기로 저장되고 글로벌 통계는 각 파티션의 시놉시스를 집계해서 생성한다.
따라서 Full Table Scan을 줄일 수 있다.
-- INCREMENTAL TRUE로 설정
SQL> EXEC DBMS_STATS.SET_TABLE_PREFS('GHLEE', 'RTE_BAK_20170113', 'INCREMENTAL', 'TRUE');
-- 일반 테이블에 INCREMENTAL 설정
SQL> EXEC DBMS_STATS.SET_TABLE_PREFS('GHLEE', 'RTE_BAK_20170113', 'INCREMENTAL_LEVEL', 'TABLE');
-- 통계 정보 수집
SQL> exec dbms_stats.gather_table_stats('GHLEE', 'RTE_BAK_20170113');
-- PARTITION TABLE에 INCREMENTAL TRUE 설정
SQL> exec dbms_stats.set_table_prefs('GHLEE', 'RTE_BACKUP', 'INCREMENTAL', 'TRUE');
-- 파티션 통계 정보 수집
SQL> exec dbms_stats.gather_table_stats('GHLEE', 'RTE_BACKUP','RTE_BACKUP_20160224', granularity => 'partition');
-- 파티션 exchange 수행
SQL> alter table CP_LG_RTE_BACKUP exchange partition CP_LG_RTE_BACKUP_20160224 with table RTE_BAK_20170113;
-- 비 파티션 테이블의 시놉시스는 파티션 테이블로 이동되고 글로벌 통계도 업데이트가 되므로, Exhcnage 한 후에 GRADE 파티션 테이블에 대해 통계 정보를 수집할 필요성이 없어진다.
Extended 파티션 Syntax
INSERT INTO mypart_tab PARTITION part_1 VALUES (1,'Some Text');
GATHER AUTO
exec dbms_stats.gather_schema_stats(ownname => 'SCOTT', options => 'GATHER AUTO');
GATHER AUTO
SQL> CREATE TABLE MY_OBJECTS_STATS AS SELECT /*+ GATHER_OPTIMIZER_STATISTICS */ * FROM MY_OBJECTS; ==> Default behavior ;hint is of no help here.
SQL> CREATE TABLE MY_OBJECTS_NOSTATS_HINT AS SELECT /*+ NO_GATHER_OPTIMIZER_STATISTICS */ * FROM MY_OBJECTS; ==> Hint instructing Oracle not to collect Stats
참고자료
11g에서는 통계정보의 'Gathering'과 'Publish'를 분리할 수 있게 되어, 검증된 통계정보만 'Publish'되도록 하여 어플리케이션 성능을 보장받을 수 있음.
SELECT DBMS_STATS.GET_PREFS('PUBLISH') PUBLISH
FROM DUAL;
– TRUE : 통계정보가 수집되자마자 바로 Publish
– FALSE : 수집된 통계정보는 Publish 되지 않고 Pending
참고 사이트 : http://wiki.gurubee.net/pages/viewpage.action?pageId=14221432
SQL> select dbms_stats.report_gather_schema_stats('GHLEE', DETAIL_LEVEL=>'TYPICAL')
2 from dual;
Statistics Collector는 Adaptive Plan 사용 시에 옵티마이저에 의해서 생성되는 Row Source Operation의 일종이다. 최초 Plan 생성 시에 삽입되고 로우 버퍼링, 통계 정보 비교, 대안 PLAN 결정을 수행한다.
Adaptive Plan 사용 설정
Adaptive Plan 조회 : dbms_xplan.display_cursor를 이용하여 조회 가능
SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'all +note +adaptive'));
SQL> select substr(sql_text, 1, 50), is_resolved_adaptive_plan from v$sql
2 where is_resolved_adaptive_plan = 'Y'
SUBSTR(SQL_TEXT,1,50) I
---------------------------------------------------------------------------------------------------- -
SELECT /*+ CONNECT_BY_FILTERING */ s.privilege# FR Y
SELECT COUNT(*) FROM DBA_SCHEDULER_WINDOWS A, DBA_ Y
-- is_resolved_adaptive_plan 컬럼을 통해 SQL문이 기존의 계획인 Adaptive Plan인지를 파악하는데 도움 될 수 있음
조인방법
SQL> create table testing_p(id number, the_value varchar2(30));
SQL> create index ix_testingP on testing_p(id);
SQL> create table testing_c(id_p number, id_t number, another_value varchar2(30));
SQL> create index ix_testingC_01 on testing_c(id_p);
SQL> create index ix_testingC_02 on testing_c(id_t);
SQL> create index ix_testingC_03 on testing_c(id_p, id_t);
SQL> select a.table_name, a.index_name, b.num_rows table_rows, a.num_rows index_rows
2 from user_indexes a, user_tables b
3 where a.table_name = b.table_name
4 and a.table_name like 'TESTING%';
TABLE_NAME INDEX_NAME TABLE_ROWS INDEX_ROWS
------------------------------ ------------------------------ ---------- ----------
TESTING_C IX_TESTINGC_02 0
TESTING_C IX_TESTINGC_03 0
TESTING_C IX_TESTINGC_01 0
TESTING_P IX_TESTINGP 0
SQL> select count(*)
2 from (
3 select b.id_t, b.id_p, b.another_value
4 from testing_P a, testing_c b
5 where a.id = b.id_p and a.id between 10001 and 10003);
SQL> select * from table(dbms_xplan.display_cursor(null, null, 'all +note +adaptive'));
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 1 (100)|
| 1 | SORT AGGREGATE | | 1 | 26 | |
| 2 | NESTED LOOPS | | 1 | 26 | 0 (0)|
|* 3 | INDEX RANGE SCAN| IX_TESTINGP | 1 | 13 | 0 (0)|
|* 4 | INDEX RANGE SCAN| IX_TESTINGC_01 | 1 | 13 | 0 (0)|
--------------------------------------------------------------------------
-----
- dynamic statistics used: dynamic sampling (level=2)
-- 통계 정보를 수행하지 않은 상태에서 data insert
SQL> truncate table testing_p;
SQL> truncate table testing_c;
SQL> declare
2 begin
3 for tt in 1..1000 loop
4 insert into testing_p values(tt, 'www.testing.com');
5 commit;
6 end loop;
7 for tt in 1..1000 loop
8 insert into testing_p values(10001, 'www.testing.com');
9 commit;
10 end loop;
11 end;
12 /
SQL> declare
2 begin
3 for aa in (select id from testing_p)
4 loop for zz in 1..3 loop
5 insert into testing_c values(aa.id, zz, 'www.testing.com');
6 end loop;
7 end loop;
8 commit;
9
10 for zz in 4..1000
11 loop
12 insert into testing_c values(1001, zz, 'www.testing.com');
13 end loop;
14 commit;
15 end;
16 /
SQL> select count(*)
2 from (
3 select b.id_t, b.id_p, b.another_value
4 from testing_P a, testing_c b
5 where a.id = b.id_p and a.id between 10001 and 10003);
SQL> select * from table(dbms_xplan.display_cursor(null, null, 'all +note +adaptive'));
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 1 (100)|
| 1 | SORT AGGREGATE | | 1 | 26 | |
| 2 | NESTED LOOPS | | 1 | 26 | 0 (0)|
|* 3 | INDEX RANGE SCAN| IX_TESTINGP | 1 | 13 | 0 (0)|
|* 4 | INDEX RANGE SCAN| IX_TESTINGC_01 | 1 | 13 | 0 (0)|
--------------------------------------------------------------------------
-----
- dynamic statistics used: dynamic sampling (level=2)
-- 통계 정보를 수행하지 않은 상태에서 data insert
SQL> truncate table testing_p;
SQL> truncate table testing_c;
SQL> declare
2 begin
3 for tt in 1..1000 loop
4 insert into testing_p values(tt, 'www.testing.com');
5 commit;
6 end loop;
7 for tt in 1..1000 loop
8 insert into testing_p values(10001, 'www.testing.com');
9 commit;
10 end loop;
11 end;
12 /
SQL> declare
2 begin
3 for aa in (select id from testing_p)
4 loop for zz in 1..3 loop
5 insert into testing_c values(aa.id, zz, 'www.testing.com');
6 end loop;
7 end loop;
8 commit;
9
10 for zz in 4..1000
11 loop
12 insert into testing_c values(1001, zz, 'www.testing.com');
13 end loop;
14 commit;
15 end;
16 /
SQL> select count(*)
2 from (
3 select b.id_t, b.id_p, b.another_value
4 from testing_P a, testing_c b
5 where a.id = b.id_p and a.id between 10001 and 10003);
SQL> select * from table(dbms_xplan.display_cursor(null, null, 'all +note +adaptive'));
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 1 (100)|
| 1 | SORT AGGREGATE | | 1 | 26 | |
| 2 | NESTED LOOPS | | 1 | 26 | 0 (0)|
|* 3 | INDEX RANGE SCAN| IX_TESTINGP | 1 | 13 | 0 (0)|
|* 4 | INDEX RANGE SCAN| IX_TESTINGC_01 | 1 | 13 | 0 (0)|
--------------------------------------------------------------------------
-----
- dynamic statistics used: dynamic sampling (level=2)
SQL> exec dbms_stats.gather_table_stats('GHLEE', 'TESTING_P', CASCADE => TRUE);
SQL> exec dbms_stats.gather_table_stats('GHLEE', 'TESTING_C', CASCADE => TRUE);
SQL> SET AUTOTRACE ON
SQL> select count(*)
2 from (
3 select b.id_t, b.id_p, b.another_value
4 from testing_P a, testing_c b
5 where a.id = b.id_p and a.id between 10001 and 10003);
COUNT(*)
----------
3000000
Elapsed: 00:00:00.24
Execution Plan
----------------------------------------------------------
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 10 | 22 (55)|
| 1 | SORT AGGREGATE | | 1 | 10 | |
|* 2 | HASH JOIN | | 3000K| 28M| 22 (55)|
| 3 | NESTED LOOPS | | 3000K| 28M| 22 (55)|
| 4 | STATISTICS COLLECTOR | | | | |
|* 5 | INDEX RANGE SCAN | IX_TESTINGP | 1000 | 5000 | 3 (0)|
|* 6 | INDEX RANGE SCAN | IX_TESTINGC_01 | 3000 | 15000 | 7 (0)|
|* 7 | INDEX FAST FULL SCAN | IX_TESTINGC_01 | 3000 | 15000 | 7 (0)|
--------------------------------------------------------------------------------
Note
-----
- 'PLAN_TABLE' is old version
* 원래 위에서 "This is an Adaptive Plan"이라는 정보가 표시되어야 하는데, 안나옴
SQL> select count(*)
2 from (
3 select b.id_t, b.id_p, b.another_value
4 from testing_P a, testing_c b
5 where a.id = b.id_p and a.id between 10001 and 10003);
SQL> select * from table(dbms_xplan.display_cursor(null, null, 'all +note +adaptive'));
---------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 22 (100)| |
| 1 | SORT AGGREGATE | | 1 | 10 | | |
| * 2 | HASH JOIN | | 3000K| 28M| 22 (55)| 00:00:01 |
|- 3 | NESTED LOOPS | | 3000K| 28M| 22 (55)| 00:00:01 |
|- 4 | STATISTICS COLLECTOR | | | | | |
| * 5 | INDEX RANGE SCAN | IX_TESTINGP | 1000 | 5000 | 3 (0)| 00:00:01 |
|- * 6 | INDEX RANGE SCAN | IX_TESTINGC_01 | 3000 | 15000 | 7 (0)| 00:00:01 |
| * 7 | INDEX FAST FULL SCAN | IX_TESTINGC_01 | 3000 | 15000 | 7 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------
Note
-----
- statistics feedback used for this statement
- this is an adaptive plan (rows marked '-' are inactive)
{code:xml}
SQL> exec dbms_stats.gather_table_stats('GHLEE', 'TESTING_P', CASCADE => TRUE);
SQL> exec dbms_stats.gather_table_stats('GHLEE', 'TESTING_C', CASCADE => TRUE);
SQL> SET AUTOTRACE ON
SQL> select count(*)
2 from (
3 select b.id_t, b.id_p, b.another_value
4 from testing_P a, testing_c b
5 where a.id = b.id_p and a.id between 10001 and 10003);
COUNT(*)
----------
3000000
Elapsed: 00:00:00.24
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 10 | 22 (55)|
| 1 | SORT AGGREGATE | | 1 | 10 | |
|* 2 | HASH JOIN | | 3000K| 28M| 22 (55)|
| 3 | NESTED LOOPS | | 3000K| 28M| 22 (55)|
| 4 | STATISTICS COLLECTOR | | | | |
|* 5 | INDEX RANGE SCAN | IX_TESTINGP | 1000 | 5000 | 3 (0)|
|* 6 | INDEX RANGE SCAN | IX_TESTINGC_01 | 3000 | 15000 | 7 (0)|
|* 7 | INDEX FAST FULL SCAN | IX_TESTINGC_01 | 3000 | 15000 | 7 (0)|
--------------------------------------------------------------------------------
Note
-----
- 'PLAN_TABLE' is old version
* 원래 위에서 "This is an Adaptive Plan"이라는 정보가 표시되어야 하는데, 안나옴
SQL> select count(*)
2 from (
3 select b.id_t, b.id_p, b.another_value
4 from testing_P a, testing_c b
5 where a.id = b.id_p and a.id between 10001 and 10003);
SQL> select * from table(dbms_xplan.display_cursor(null, null, 'all +note +adaptive'));
---------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 22 (100)| |
| 1 | SORT AGGREGATE | | 1 | 10 | | |
| * 2 | HASH JOIN | | 3000K| 28M| 22 (55)| 00:00:01 |
|- 3 | NESTED LOOPS | | 3000K| 28M| 22 (55)| 00:00:01 |
|- 4 | STATISTICS COLLECTOR | | | | | |
| * 5 | INDEX RANGE SCAN | IX_TESTINGP | 1000 | 5000 | 3 (0)| 00:00:01 |
|- * 6 | INDEX RANGE SCAN | IX_TESTINGC_01 | 3000 | 15000 | 7 (0)| 00:00:01 |
| * 7 | INDEX FAST FULL SCAN | IX_TESTINGC_01 | 3000 | 15000 | 7 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------
Note
-----
- statistics feedback used for this statement
- this is an adaptive plan (rows marked '-' are inactive)
병렬분배방법
SQL> select count(*) from
2 (select /*+ parallel(4) full(a) full(b) */
3 b.id_t, b.id_p, b.another_value
4 from testing_p a, testing_c b
5 where a.id = b.id_p
6 and a.id between 10001 and 10003);
----------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| TQ |IN-OUT| PQ Distrib |
----------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 10 | 8 (38)| | | |
| 1 | SORT AGGREGATE | | 1 | 10 | | | | |
| 2 | PX COORDINATOR | | | | | | | |
| 3 | PX SEND QC (RANDOM) | :TQ10002 | 1 | 10 | | Q1,02 | P->S | QC (RAND) |
| 4 | SORT AGGREGATE | | 1 | 10 | | Q1,02 | PCWP | |
|* 5 | HASH JOIN | | 3000K| 28M| 8 (38)| Q1,02 | PCWP | |
| 6 | PX RECEIVE | | 1000 | 5000 | 2 (0)| Q1,02 | PCWP | |
| 7 | PX SEND HYBRID HASH | :TQ10000 | 1000 | 5000 | 2 (0)| Q1,00 | P->P | HYBRID HASH|
| 8 | STATISTICS COLLECTOR | | | | | Q1,00 | PCWC | |
| 9 | PX BLOCK ITERATOR | | 1000 | 5000 | 2 (0)| Q1,00 | PCWC | |
|* 10 | TABLE ACCESS FULL | TESTING_P | 1000 | 5000 | 2 (0)| Q1,00 | PCWP | |
| 11 | PX RECEIVE | | 3000 | 15000 | 3 (0)| Q1,02 | PCWP | |
| 12 | PX SEND HYBRID HASH (SKEW)| :TQ10001 | 3000 | 15000 | 3 (0)| Q1,01 | P->P | HYBRID HASH|
| 13 | PX BLOCK ITERATOR | | 3000 | 15000 | 3 (0)| Q1,01 | PCWC | |
|* 14 | TABLE ACCESS FULL | TESTING_C | 3000 | 15000 | 3 (0)| Q1,01 | PCWP | |
----------------------------------------------------------------------------------------------------------------
기존의 높이균형 히스토그램에서 Unpopular Value 여러 개가 하나의 버킷에 저장되었다.
12c의 새로운 Hybrid 히스토그램상에서는 이런 한 버킷의 여러 Unpopular Value 중에서 가장 많은 개수를 가진 값을 Almost Popular Value라고 하고 Endpoint-Value로서 반복 횟수를 저장한다.
create table hybrid_histogram(id number, the_column number)
insert into hybrid_histogram values(1,1);
insert into hybrid_histogram values(1,2);
insert into hybrid_histogram values(1,2);
insert into hybrid_histogram values(1,2);
insert into hybrid_histogram values(1,51);
insert into hybrid_histogram values(1,51);
insert into hybrid_histogram values(1,71);
insert into hybrid_histogram values(1,81);
insert into hybrid_histogram values(1,81);
insert into hybrid_histogram values(1,82);
insert into hybrid_histogram values(1,83);
insert into hybrid_histogram values(1,83);
commit;
exec dbms_stats.gather_table_stats(null, 'HYBRID_HISTOGRAM', method_opt => 'for columns the_column size 3');
select table_name, column_name, num_distinct, histogram
from user_tab_col_statistics
where table_name = 'HYBRID_HISTOGRAM';
TABLE_NAME COLUMN_NAM NUM_DISTINCT HISTOGRAM
------------------------------ ---------- ------------ ---------------
HYBRID_HISTOGRAM THE_COLUMN 7 HYBRID
select owner, table_name, column_name, endpoint_value, endpoint_repeat_count
from dba_histograms
where owner = 'GHLEE' and table_name = 'HYBRID_HISTOGRAM'
order by 1, 2, 3, 4;
OWNER TABLE_NAME COLUMN_NAM ENDPOINT_VALUE ENDPOINT_REPEAT_COUNT
---------- ------------------------------ ---------- -------------- ---------------------
GHLEE HYBRID_HISTOGRAM THE_COLUMN 1 1
GHLEE HYBRID_HISTOGRAM THE_COLUMN 51 2
GHLEE HYBRID_HISTOGRAM THE_COLUMN 83 2