SELECT val1, count(*), ratio_to_report(count(*)) OVER ()*100 AS percent
2 FROM t
3 GROUP BY val1
4 ORDER BY val1;
VAL1 COUNT(*) PERCENT
---------- ---------- -------
39 2 0.2
41 4 0.4
42 13 1.3
43 21 2.1
44 26 2.6
45 54 5.4
46 66 6.6
47 86 8.6
48 81 8.1
49 97 9.7
50 102 10.2
51 103 10.3
52 80 8.0
53 64 6.4
54 76 7.6
55 50 5.0
56 30 3.0
57 21 2.1
58 12 1.2
59 6 0.6
60 5 0.5
63 1 0.1
SYS@2017-11-19 22:57:52> SELECT endpoint_value, endpoint_number,
2 endpoint_number - lag(endpoint_number,1,0)
3 OVER (ORDER BY endpoint_number) AS count,
4 endpoint_repeat_count
5 FROM user_tab_histograms
6 WHERE table_name = 'T'
7 AND column_name = 'VAL1'
8 ORDER BY endpoint_number;
ENDPOINT_VALUE ENDPOINT_NUMBER COUNT ENDPOINT_REPEAT_COUNT
-------------- --------------- ---------- ---------------------
39 2 2 2
44 66 64 26
45 120 54 54
46 186 66 66
47 272 86 86
48 353 81 81
49 450 97 97
50 552 102 102
51 655 103 103
52 735 80
SYS@2017-11-19 22:59:55> EXPLAIN PLAN SET STATEMENT_ID '44' FOR SELECT * FROM t WHERE val1 = 44;
SYS@2017-11-19 22:59:55> EXPLAIN PLAN SET STATEMENT_ID '50' FOR SELECT * FROM t WHERE val1 = 50;
SYS@2017-11-19 22:59:55> EXPLAIN PLAN SET STATEMENT_ID '56' FOR SELECT * FROM t WHERE val1 = 56;
SYS@2017-11-19 22:59:55>
SYS@2017-11-19 22:59:55> COLUMN statement_id FORMAT A12
SYS@2017-11-19 22:59:55>
SYS@2017-11-19 22:59:55> SELECT statement_id, cardinality
2 FROM plan_table
3 WHERE id = 0
4 ORDER BY statement_id;
STATEMENT_ID CARDINALITY
------------ -----------
44 26
50 102
56 30
SYS@2017-11-19 22:38:15> SELECT endpoint_value, endpoint_number
2 FROM user_tab_histograms
3 WHERE table_name = 'T'
4 AND column_name = 'ID'
5 ORDER BY endpoint_number;
ENDPOINT_VALUE ENDPOINT_NUMBER
-------------- ---------------
1 0
1000 1
SYS@2017-11-19 22:38:15>
JIGI@2017-11-19 23:11:07> SELECT dbms_stats.create_extended_stats(ownname => user,
2 tabname => 'T',
3 extension => '(upper(pad))') AS ext1,
4 dbms_stats.create_extended_stats(ownname => user,
5 tabname => 'T',
6 extension => '(val2,val3)') AS ext2
7 FROM dual;
EXT1
----------------------------
EXT2
----------------------------
SYS_STU0KSQX64#I01CKJ5FPGFK3W9
SYS_STUPS77EFBJCOTDFMHM8CHP7Q1
JIGI@2017-11-19 23:16:47> SELECT extension_name, extension
2 FROM user_stat_extensions
3 WHERE table_name = 'T';
EXTENSION_NAME EXTENSION
------------------------------ ---------------
SYS_STU0KSQX64#I01CKJ5FPGFK3W9 (UPPER("PAD"))
SYS_STUPS77EFBJCOTDFMHM8CHP7Q1 ("VAL2","VAL3")
JIGI@2017-11-19 23:17:59> SELECT column_name, data_type, hidden_column, data_default
2 FROM user_tab_cols
3 WHERE table_name = 'T'
4 ORDER BY column_id;
COLUMN_NAME DATA_TYPE HIDDEN
------------------------------ --------- ------
DATA_DEFAULT
--------------------------------------------------------------------------------
ID NUMBER NO
VAL1 NUMBER NO
VAL2 NUMBER NO
VAL3 NUMBER NO
PAD VARCHAR2 NO
SYS_STU0KSQX64#I01CKJ5FPGFK3W9 VARCHAR2 YES
UPPER("PAD")
SYS_STUPS77EFBJCOTDFMHM8CHP7Q1 NUMBER YES
SYS_OP_COMBINED_HASH("VAL2","VAL3")
JIGI@2017-11-19 23:20:58> BEGIN
2 dbms_stats.drop_extended_stats(ownname => 'JIGI',
3 tabname => 'T',
4 extension => '(upper(pad))');
5 dbms_stats.drop_extended_stats(ownname => 'JIGI',
6 tabname => 'T',
7 extension => '(val2,val3)');
8 END;
9 /
SYS@2017-11-19 23:23:14> BEGIN
2 dbms_stats.seed_col_usage(sqlset_name => NULL,
3 owner_name => NULL,
4 time_limit => 30);
5 END;
6 /
SYS@2017-11-19 23:23:15>
JIGI@2017-11-19 23:27:57> SELECT dbms_stats.create_extended_stats(ownname => user, tabname => 't')
2 FROM dual;
DBMS_STATS.CREATE_EXTENDED_STATS(OWNNAME=>USER,TABNAME=>'T')
--------------------------------------------------------------------------------
###############################################################################
JIGI@2017-11-19 23:27:57>
JIGI@2017-11-19 23:27:57> PAUSE
JIGI@2017-11-19 23:28:02>
JIGI@2017-11-19 23:28:02> SELECT column_name, data_type, hidden_column, data_default
2 FROM user_tab_cols
3 WHERE table_name = 'T'
4 ORDER BY column_id;
COLUMN_NAME DATA_TYPE HIDDEN DATA_DEFAULT
------------------------------ --------- ------ -----------------------------------
VAL1 NUMBER NO
VAL2 NUMBER NO
VAL3 NUMBER NO
VAL4 NUMBER NO
SYS_STU4K1K3JNH1Z9#_L_V93K3DT4 NUMBER YES SYS_OP_COMBINED_HASH("VAL1","VAL2")
SYS_STUPS77EFBJCOTDFMHM8CHP7Q1 NUMBER YES SYS_OP_COMBINED_HASH("VAL2","VAL3")
SYS_STUSS3AZ43WO3NKM#66PIG2JYW NUMBER YES SYS_OP_COMBINED_HASH("VAL3","VAL4")
SYS_STUS574STTDWYBF6PGQN#XHGGJ NUMBER YES SYS_OP_COMBINED_HASH("VAL1","VAL3")
JIGI@2017-11-19 23:21:42> CREATE TABLE persons (
2 name VARCHAR2(100),
3 name_upper AS (upper(name))
4 );
JIGI@2017-11-19 23:21:42>
JIGI@2017-11-19 23:21:42> INSERT INTO persons (name) VALUES ('Michelle');
JIGI@2017-11-19 23:21:42>
JIGI@2017-11-19 23:21:42> SELECT name
2 FROM persons
3 WHERE name_upper = 'MICHELLE';
NAME
----------
Michelle
JIGI@2017-11-19 23:40:33> SELECT index_name AS name,
2 blevel,
3 leaf_blocks AS leaf_blks,
4 distinct_keys AS dst_keys,
5 num_rows,
6 clustering_factor AS clust_fact,
7 avg_leaf_blocks_per_key AS leaf_per_key,
8 avg_data_blocks_per_key AS data_per_key
9 FROM user_ind_statistics
10 WHERE table_name = 'T';
NAME BLEVEL LEAF_BLKS DST_KEYS NUM_ROWS CLUST_FACT LEAF_PER_KEY DATA_PER_KEY
---------- ------ --------- -------- -------- ---------- ------------ ------------
T_VAL2_I 1 3 6 1000 187 1 31
T_VAL1_I 1 2 22 1000 510 1 23
T_PK 1 2 1000 1000 982 1 1
SYS_IL0000
093649C000
05$$
JIGI@2017-11-19 23:53:04> CREATE TABLE t (id NUMBER, p DATE, sp NUMBER, pad VARCHAR2(1000))
2 PARTITION BY RANGE (p)
3 SUBPARTITION BY HASH (sp) SUBPARTITION TEMPLATE
4 (
5 SUBPARTITION sp1,
6 SUBPARTITION sp2,
7 SUBPARTITION sp3,
8 SUBPARTITION sp4
9 )
10 (
11 PARTITION q1 VALUES LESS THAN (to_date('2013-04-01','YYYY-MM-DD')),
12 PARTITION q2 VALUES LESS THAN (to_date('2013-07-01','YYYY-MM-DD')),
13 PARTITION q3 VALUES LESS THAN (to_date('2013-10-01','YYYY-MM-DD')),
14 PARTITION q4 VALUES LESS THAN (to_date('2014-01-01','YYYY-MM-DD'))
15 );
대상 오브젝트 파라미터는 아래와 같이 어떤 오브젝트에 대한 통계를 수집할지 지정한다.
스테일(stale) : 통계와 실제 데이터의 불일치가 심해져서 통계정보가 부정확하다라는 의미
오라클 엔진은 기본적으로 데이터가 10%이상 변경되었을 경우 스테일 상태로 판정한다.
dbms_stats.create_stat_table(ownname => user, stattab => 'MYSTATS', tblspace => 'USERS');
dbms_stats.upgrade_stat_table(ownname => user, stattab => 'MYSTATS');
dbms_stats.upgrade_stat_table(ownname => user, stattab => 'MYSTATS');
dbms_stats.drop_stat_table(ownname => user, stattab => 'MYSTATS');
exec dbms_stats.set_param(pname => 'CASCADE', pval => 'TRUE')
dbms_stats.set_database_prefs(pname => 'CASCADE', pvalue => 'DBMS_STATS.AUTO_CASCADE');
dbms_stats.set_table_prefs(ownname => 'SCOTT',
pname => 'CASCADE',
pvalue => 'FALSE');
dbms_stats.delete_schema_prefs(ownname => 'scott', pname => 'cascade');
-- PUBLISH를 FALSE로 하여 통계에 자동반영되지 않도록 설정한다.
dbms_stats.set_table_prefs(
ownname => user,
tabname => 't',
pname => 'publish',
pvalue => 'false'
);
dbms_stats.gather_table_stats(
ownname => user,
tabname => 'T',
estimate_percent => 100,
method_opt => 'for all columns size 1',
cascade => TRUE
);
-- 세션이나 힌트등을 사용해 통계가 미치는 영향을 테스트 한다.
SELECT /*+ opt_param('optimizer_use_pending_statistics' 'true') */ * FROM t;
ALTER SESSION SET optimizer_use_pending_statistics = TRUE;
-- 테스트가 성공적이면 딕셔너리에 반영한다.
dbms_stats.publish_pending_stats(ownname => user, tabname => 'T');
-- 테스트에 실패했다면 통계를 삭제한다.
dbms_stats.delete_pending_stats(ownname => user, tabname => 'T');
-- 통계수집 시 딕셔너리에 자동반영되도록 상태를 설정한다.
dbms_stats.set_table_prefs(
ownname => user,
tabname => 't',
pname => 'publish',
pvalue => 'true'
);
-- 서브파티션 레벨에서만 통계를 수집하도록 한다(파생통계)
JIGI@2017-11-20 01:38:37> BEGIN
2 dbms_stats.delete_table_stats(ownname => user,
3 tabname => 't');
4 dbms_stats.gather_table_stats(ownname => user,
5 tabname => 't',
6 estimate_percent => 100,
7 granularity => 'subpartition');
8 END;
9 /
JIGI@2017-11-20 01:39:50> SELECT count(DISTINCT sp)
2 FROM t;
COUNT(DISTINCTSP)
-----------------
100
-- 글로벌레벨의 통계가 부정확한 것을 확인 할 수 있다.
JIGI@2017-11-20 01:39:50> SELECT num_distinct, global_stats
2 FROM user_tab_col_statistics
3 WHERE table_name = 'T'
4 AND column_name = 'SP';
NUM_DISTINCT GLOBAL_STATS
------------ ------------
28 NO
-- 파티션 레벨의 통계도 부정확한 것을 확인할 수 있다.
JIGI@2017-11-20 01:41:17> SELECT num_distinct, global_stats
2 FROM user_part_col_statistics
3 WHERE table_name = 'T'
4 AND partition_name = 'Q1'
5 AND column_name = 'SP';
NUM_DISTINCT GLOBAL_STATS
------------ ------------
28 NO
JIGI@2017-11-20 01:41:45> SELECT 'Q1_SP1' AS subpartition_name, count(DISTINCT sp) FROM t SUBPARTITION (q1_sp1)
2 UNION ALL
3 SELECT 'Q1_SP2', count(DISTINCT sp) FROM t SUBPARTITION (q1_sp2)
4 UNION ALL
5 SELECT 'Q1_SP3', count(DISTINCT sp) FROM t SUBPARTITION (q1_sp3)
6 UNION ALL
7 SELECT 'Q1_SP4', count(DISTINCT sp) FROM t SUBPARTITION (q1_sp4);
SUBPARTITION_NAME COUNT(DISTINCTSP)
----------------- -----------------
Q1_SP1 20
Q1_SP2 28
Q1_SP3 25
Q1_SP4 27
-- 파리션 레벨의 통계는 정확한 것을 확인할 수 있다.
JIGI@2017-11-20 01:41:45> SELECT subpartition_name, num_distinct, global_stats
2 FROM user_subpart_col_statistics
3 WHERE table_name = 'T'
4 AND column_name = 'SP'
5 AND subpartition_name LIKE 'Q1%'
6 ORDER BY subpartition_name;
SUBPARTITION_NAME NUM_DISTINCT GLOBAL_STATS
----------------- ------------ ------------
Q1_SP1 20 YES
Q1_SP2 28 YES
Q1_SP3 25 YES
Q1_SP4 27 YES
dbms_stats.copy_tables_stats(ownname => user,
tabname => 't',
srcpartname => 'p_2014_q1',
dstpartname => 'p_2015_q1',
scale_factor => 1);
-- job을 시작하거나 중지하는 방법
dbms_scheduler.enable(name => 'sys.gather_stats_job');
dbms_scheduler.disble(name => 'sys.gather_stats_job');
-- sys 유저 이외의 사용자에게 job실행권한을 줄때
grant alter on gather_stats_job to system;
-- 유지보수 작업을 활성화하거나 비활성화 하기
dbms_auto_task_admin.enable(client_name => 'auto optimizer stats collection',
operation => null,
windows_name => null);
dbms_auto_task_admin.disable(client_name => 'auto optimizer stats collection',
operation => null,
windows_name => null)
-- 실행계획의 기본 보존기간은 31일 이다.
JIGI@2017-11-20 22:19:07> select dbms_stats.get_stats_history_retention() as retention from dual;
RETENTION
----------
31
-- 보존기간을 14일로 조정한다.
JIGI@2017-11-20 22:20:31> exec dbms_stats.alter_stats_history_retention(retention => 14);
PL/SQL procedure successfully completed.
JIGI@2017-11-20 22:20:49> select dbms_stats.get_stats_history_retention() as retention from dual;
RETENTION
----------
14
-- 14일이 지난 통계를 제거한다.
JIGI@2017-11-20 22:22:27> exec dbms_stats.purge_stats(before_timestamp => systimestamp - INTERVAL '14' DAY);
PL/SQL procedure successfully completed.
-- 수집된 통계 시점 확인
IGI@2017-11-20 22:26:02> select stats_update_time from dba_tab_stats_history where owner = 'SYS' and table_name='TAB$';
STATS_UPDATE_TIME
---------------------------------------------------------------------------
20-NOV-17 12.20.53.387586 AM +09:00
-- SH스키마의 오브젝트 통계를 하루 전으로 복원한다.
JIGI@2017-11-20 22:31:59> exec dbms_stats.restore_schema_stats(ownname => 'SH', as_of_timestamp => systimestamp - INTERVAL'1' day, force => TRUE);
PL/SQL procedure successfully completed.
-- 특정 스키마의 모든 오브젝트 통계를 잠근다.
dbms_stats.lock_schema_stats(ownname => user);
-- 특정 테이블의 통계를 잠근다.
exec dbms_stats.lock_table_stats(ownname => user, tabname => 'T');
-- 특정 스키마의 모든 오브젝트 통계 잠금을 연다.
exec dbms_stats.unlock_schema_stats(ownname => user);
-- 특정 테이블의 오브텍트 통계잠금을 연다.
exec dbms_stats.unlock_table_stats(ownname => user, tabname => 'T');
dbms_stats.gather_table_stats(ownname => user,
tabname => 'T',
force => TRUE);
- 지정된 백업테이블과 현재 오브젝트 통계와 비교하거나, 다른 백업 테이블과 비교한다.
exec dbms_stats.diff_table_stats_in_stattab(ownname => user,
tabname => 'T',
stattab1 => 'mystats',
statid1 => 'set1',
stattablown => user, pctthreshold => 10);
-- 지정된 테이블의 현재와 과거의 이력을 비교하거나(time2를 null로 설정), 두개의 과거의 서로다른 이력을 비교한다.
exec dbms_stats.diff_table_stats_in_history(ownname => user,
tabname => 'T',
time1 => systimestamp - 1,
time2 => null,
pctthreshold => 10);
-- 지정된 테이블의 현재(또는 과거) 통계와 펜딩통계를 비교한다.
exec dbms_stats.diff_table_stats_in_pending(ownname => user,
tabname => 'T',
time_stamp => null,
pctthreshold => 10);
-- 통계를 수집한 시간을 확인 할 수 있다.
select operation, start_time, (end_time-start_time) day(1) to second(0) as duration
2 from dba_optstat_operations
3* order by start_time desc
OPERATION START_TIME DURATION
------------------------------ ---------------------------------------- ------------------------------
gather_table_stats 20-NOV-17 10.53.52.896611 PM +09:00 +0 00:00:01
restore_table_stats 20-NOV-17 10.53.41.867056 PM +09:00 +0 00:00:01
gather_table_stats 20-NOV-17 10.50.28.145428 PM +09:00 +0 00:00:01
gather_table_stats 20-NOV-17 10.50.24.011018 PM +09:00 +0 00:00:02
unlock_schema_stats 20-NOV-17 10.44.01.708343 PM +09:00 +0 00:00:00
unlock_table_stats 20-NOV-17 10.43.43.739795 PM +09:00 +0 00:00:00
delete_table_stats 20-NOV-17 10.43.36.255505 PM +09:00 +0 00:00:01
gather_table_stats 20-NOV-17 10.41.00.101852 PM +09:00 +0 00:00:01
gather_table_stats 20-NOV-17 10.40.35.980898 PM +09:00 +0 00:00:00
gather_schema_stats 20-NOV-17 10.40.21.782940 PM +09:00 +0 00:00:00
lock_schema_stats 20-NOV-17 10.40.12.187797 PM +09:00 +0 00:00:00
gather_table_stats 20-NOV-17 10.39.57.475777 PM +09:00 +0 00:00:01
unlock_schema_stats 20-NOV-17 10.37.42.033266 PM +09:00 +0 00:00:00
lock_table_stats 20-NOV-17 10.36.45.160291 PM +09:00 +0 00:00:00
lock_schema_stats 20-NOV-17 10.36.17.903805 PM +09:00 +0 00:00:00