Você está na página 1de 16

1, what oracle document tells us?

For partitioned tables, when we collect statistics, even when only one partition changed, such as
add a new partition or load some data to a partition,
we have to scan all partitions to compute global statistics, this is much resource intensive
operation.
In 11G, oracle introduced incremental statistics gather option for partitioned table. When a
partitioned table is set to incremental attribute, it will store
a synopsis on sysaux tablespace for global level statistics(it will only consume very little
space), then, it will only scan one partition but will update
the global statistics, which will save many resource.

2, my test:
2.0: My Test Environments:
Oracle version is: 11.1.0.6,
OS: Solaris

2.1 create test table:


Let us create 2 tables test_inc(enable incremental statistics) and test_inc_bak(disable
incremental statistics) with same structure:
SQL> create table test_inc
test_inc(a number, b
varchar2(100), partition_key number)
2 partition by range(partition_key)
3 (
4
partition p00 values less than
5
partition p01 values less than
6
partition p02 values less than
7
partition p03 values less than
8
partition p04 values less than
9
partition p05 values less than
10
partition p06 values less than
11
partition p07 values less than
12
partition p08 values less than
13
partition p09 values less than
14
partition p10 values less than
15
partition p11 values less than
16
partition p12 values less than
17
partition p13 values less than
18
partition p14 values less than
19
partition p15 values less than
20
partition p16 values less than
21
partition p17 values less than
22
partition p18 values less than
23
partition p19 values less than
24
partition p20 values less than
25
partition p21 values less than
26
partition p22 values less than
27
partition p23 values less than
28
partition p24 values less than
29
partition p25 values less than
30
partition p26 values less than
31
partition p27 values less than
32
partition p28 values less than

date, c varchar2(30), d varchar2(100), e

(1),
(2),
(3),
(4),
(5),
(6),
(7),
(8),
(9),
(10),
(11),
(12),
(13),
(14),
(15),
(16),
(17),
(18),
(19),
(20),
(21),
(22),
(23),
(24),
(25),
(26),
(27),
(28),
(29),

33
34
35
36

partition p29 values less than (30),


partition p30 values less than (31),
partition pmax values less than(maxvalue)
) ;

SQL> create table test_inc_bak


test_inc_bak(a number, b date, c varchar2(30), d
varchar2(100), e varchar2(100), partition_key number)
2 partition by range(partition_key)
3 (
4
partition p00 values less than (1),
5
partition p01 values less than (2),
6
partition p02 values less than (3),
7
partition p03 values less than (4),
8
partition p04 values less than (5),
9
partition p05 values less than (6),
10
partition p06 values less than (7),
11
partition p07 values less than (8),
12
partition p08 values less than (9),
13
partition p09 values less than (10),
14
partition p10 values less than (11),
15
partition p11 values less than (12),
16
partition p12 values less than (13),
17
partition p13 values less than (14),
18
partition p14 values less than (15),
19
partition p15 values less than (16),
20
partition p16 values less than (17),
21
partition p17 values less than (18),
22
partition p18 values less than (19),
23
partition p19 values less than (20),
24
partition p20 values less than (21),
25
partition p21 values less than (22),
26
partition p22 values less than (23),
27
partition p23 values less than (24),
28
partition p24 values less than (25),
29
partition p25 values less than (26),
30
partition p26 values less than (27),
31
partition p27 values less than (28),
32
partition p28 values less than (29),
33
partition p29 values less than (30),
34
partition p30 values less than (31),
35
partition pmax values less than(maxvalue)
36 );

2.2 set incremental attribute for one table:


Let enable incremental statistics gathering for test_inc:
SQL> select DBMS_STATS.get_PREFS('incremental', 'wwf', 'test_inc') from
dual;
DBMS_STATS.GET_PREFS('INCREMENTAL','WWF','TEST_INC')
FALSE
SQL> exec
DBMS_STATS.SET_TABLE_PREFS(user,'test_inc','INCREMENTAL','TRUE')
PL/SQL procedure successfully completed.
SQL> select DBMS_STATS.get_PREFS('incremental', 'wwf', 'test_inc') from
dual;
DBMS_STATS.GET_PREFS('INCREMENTAL','WWF','TEST_INC')

TRUE

For test_inc_bak, keep it disabled for incremental statistics gathering:


SQL> select DBMS_STATS.get_PREFS('incremental', 'wwf', 'test_inc_bak') from dual;
DBMS_STATS.GET_PREFS('INCREMENTAL','WWF','TEST_INC_BAK')
--------------------------------------------------------------------------------------------------------------------------------FALSE

2.3 The whole test process:


populate 300k records for each
Then, using the following sql to populate data for each partition(populate
partition and change the value of partition key correspondingly
correspondingly):
insert into test_inc select rownum, sysdate+mod(rownum, 30),
'wangweifengwangweifeng'||rownum,
rpad('x', 50, 'x')||rownum, rpad('y', 50, 'z')||rownum, 0 from dual connect by
rownum <= 300000
300000;
insert into test_inc_bak select rownum, sysdate+mod(rownum, 30),
'wangweifengwangweifeng'||rownum,
rpad('x', 50, 'x')||rownum, rpad('y', 50, 'z')||rownum, 0 from dual connect by
rownum <= 300000
300000;

My test indicates that: the incremental attribute will make the statistics gathering process much
faster!
exec dbms_stats.gather_table_stats(user, 'test_inc')
exec dbms_stats.gather_table_stats(user, 'test_inc_bak')

2.4 test result and compare


The following table is the time spent for incremental/non-incremental tables statistics-gathering
process:
partition
number
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

Time elapsed for incremental table


seconds
test_inc(
test_inc(seconds
seconds))
4.37
4.47
5.09
5.38
5.47
5.76
6.13
6.62
7.28
7.32
7.53
7.86
8.62
8.89
9.48
10.01
10.77

Time elapsed for non-incremental table


test_inc_bak (seconds
seconds))
4.36
8.66
13.2
19.48
24.06
29.39
34.03
38.97
46.24
50.75
56.83
62.25
67.06
72.73
78.93
83.66
87.44

18
19
20
21
22
23
24
25
26
27
28
29
30
31

11.22
10.72
11.48
11.01
11.06
10.68
12.58
8.01
12.38
13.09
14.67
13.55
14.27
14.15

Use the above data, let


lets plot picture:
We can see, for non-incremental partitioned table, the time elapsed for non-incremental (the
yellow line) is linear with the partition number.
While for the incremental partitioned table, the time elapsed (the red line) is nearly a flat line:

The red line represent time elapsed for incremental partitioned table
The yellow line time elapsed for non-incremental partitioned table

93.74
100.85
105.54
112.08
116.53
123.57
126.58
135.41
137.95
144.33
149.75
153.94
160.50
165.50

2.5: let
lets see from another perspective: what last_analyzed column can tell
us?
SQL> exec dbms_stats.gather_table_stats(user, 'test_inc'
'test_inc')
PL/SQL procedure successfully completed.
Elapsed: 00:00:04.37
SQL> select partition_name, blocks, num_rows, last_analyzed from
user_tab_partitions where table_name = 'TEST_INC' and num_rows > 0;
PARTITION_NAME
BLOCKS
NUM_ROWS LAST_ANALYZED
------------------------------ ---------- ---------- ------------------P00
6796
300000 2009-09-09 00:49:51

SQL> select table_name, blocks, num_rows, last_analyzed from


user_tables where table_name = 'TEST_INC';
TABLE_NAME
BLOCKS
NUM_ROWS LAST_ANALYZED
------------------------------ ---------- ---------- ------------------TEST_INC
6796
300000 2009-09-09 00:49:51

Insert 300k records for empty partition p2:


SQL> exec dbms_stats.gather_table_stats(user, 'test_inc')
PL/SQL procedure successfully completed.
Elapsed: 00:00:04.47
SQL> select partition_name, blocks, num_rows, last_analyzed from
user_tab_partitions where table_name = 'TEST_INC' and num_rows > 0;
PARTITION_NAME
BLOCKS
NUM_ROWS LAST_ANALYZED
------------------------------ ---------- ---------- ------------------P00
6796
300000 2009-09-09 00:49:51
P01
6796
300000 2009-09-09 00:52:27
SQL> select table_name, blocks, num_rows, last_analyzed from user_tables where
table_name = 'TEST_INC';
TABLE_NAME
BLOCKS
NUM_ROWS LAST_ANALYZED
------------------------------ ---------- ---------- ------------------600000 2009-09-09 00:52:29
TEST_INC
13592

Insert 300k records for empty partition p3:


SQL> exec dbms_stats.gather_table_stats(user, 'test_inc')
PL/SQL procedure successfully completed.
Elapsed: 00:00:05.09
SQL> select partition_name, blocks, num_rows, last_analyzed from
user_tab_partitions where table_name = 'TEST_INC' and num_rows > 0;
PARTITION_NAME
BLOCKS
NUM_ROWS LAST_ANALYZED
------------------------------ ---------- ---------- ------------------P00
6796
300000 2009-09-09 00:49:51
P01
6796
300000 2009-09-09 00:52:27
P02
6796
300000 2009-09-09 00:55:39

SQL> select table_name, blocks, num_rows, last_analyzed from user_tables where


table_name = 'TEST_INC';
TABLE_NAME
BLOCKS
NUM_ROWS LAST_ANALYZED
------------------------------ ---------- ---------- ------------------TEST_INC
20388
900000 2009-09-09 00:55:40

Insert 300k records for empty partition p4:


SQL> exec dbms_stats.gather_table_stats(user, 'test_inc')
PL/SQL procedure successfully completed.
Elapsed: 00:00:05.38
SQL> select partition_name, blocks, num_rows, last_analyzed from
user_tab_partitions where table_name = 'TEST_INC'
2 and num_rows > 0;
PARTITION_NAME
BLOCKS
NUM_ROWS LAST_ANALYZED
------------------------------ ---------- ---------- ------------------P00
6796
300000 2009-09-09 00:49:51
P01
6796
300000 2009-09-09 00:52:27
P02
6796
300000 2009-09-09 00:55:39
P03
6796
300000 2009-09-09 01:00:23

SQL> select table_name, blocks, num_rows, last_analyzed from


user_tables where table_name = 'TEST_INC';
TABLE_NAME
BLOCKS
NUM_ROWS LAST_ANALYZED
------------------------------ ---------- ---------- ------------------TEST_INC
27184
1200000 2009-09-09 01:00:24

We can see that: the last_analyzed column won


wont change after each analyze.
This can tell us that: the unchanged analyzed partition is skipped when enable incremental
statistics gathering.
But, let
lets see the last_analyzed column for non-incremental partitions table:
The last_analyze column changes when every analyze: this is the last time I analyzed the
non-incremental table test_inc_bak(I started my test from 09/09/2009):
SQL> select partition_name, num_rows, blocks, last_analyzed from
user_tab_partitions where table_name = 'TEST_INC_BAK'
'TEST_INC_BAK';
PARTITION_NAME
NUM_ROWS
BLOCKS LAST_ANALYZED
------------------------------ ---------- ---------- ------------------P00
300000
6796 2009-09-11 01:07:43
P01
300000
6796 2009-09-11 01:07:46
P02
300000
6796 2009-09-11 01:07:49
P03
300000
6796 2009-09-11 01:07:52
P04
300000
6796 2009-09-11 01:07:54
P05
300000
6796 2009-09-11 01:07:57
P06
300000
6796 2009-09-11 01:08:00
P07
300000
6796 2009-09-11 01:08:03
P08
300000
6796 2009-09-11 01:08:06
P09
300000
6796 2009-09-11 01:08:09
P10
300000
6796 2009-09-11 01:08:13
P11
300000
6796 2009-09-11 01:08:16
P12
300000
6796 2009-09-11 01:08:19
P13
300000
6796 2009-09-11 01:08:22
P14
300000
6796 2009-09-11 01:08:24
P15
300000
6796 2009-09-11 01:08:27

P16
P17
P18
P19
P20
P21
P22
P23
P24
P25
P26
P27
P28
P29
P30
PMAX

300000
300000
300000
300000
300000
300000
300000
300000
300000
300000
300000
300000
300000
300000
300000
0

6796
6796
6796
6796
6796
6796
6796
6796
6796
6796
6796
6796
6796
6796
6796
0

2009-09-11
2009-09-11
2009-09-11
2009-09-11
2009-09-11
2009-09-11
2009-09-11
2009-09-11
2009-09-11
2009-09-11
2009-09-11
2009-09-11
2009-09-11
2009-09-11
2009-09-11
2009-09-11

01:08:30
01:08:33
01:08:36
01:08:39
01:08:42
01:08:45
01:08:48
01:08:51
01:08:54
01:08:57
01:09:00
01:09:03
01:09:06
01:09:09
01:09:11
01:09:11

While the following is what analyze looks like when I finish the analyze on incremental
table test_inc_bak:
SQL> select partition_name, num_rows, blocks, last_analyzed from
user_tab_partitions where table_name = 'TEST_INC'
'TEST_INC';
PARTITION_NAME
NUM_ROWS
BLOCKS LAST_ANALYZED
------------------------------ ---------- ---------- ------------------P00
300000
6796 2009-09-09 00:49:51
P01
300000
6796 2009-09-09 00:52:27
P02
300000
6796 2009-09-09 00:55:39
P03
300000
6796 2009-09-09 01:00:23
P04
300000
6796 2009-09-09 01:04:59
P05
300000
6796 2009-09-09 01:09:37
P06
300000
6796 2009-09-09 01:14:06
P07
300000
6796 2009-09-09 01:17:33
P08
300000
6796 2009-09-09 01:22:14
P09
300000
6796 2009-09-09 01:28:15
P10
300000
6796 2009-09-09 01:33:29
P11
300000
6796 2009-09-09 01:36:36
P12
300000
6796 2009-09-09 01:42:39
P13
300000
6796 2009-09-09 01:46:54
P14
300000
6796 2009-09-09 01:55:36
P15
300000
6796 2009-09-09 02:04:55
P16
300000
6796 2009-09-09 02:20:34
P17
300000
6796 2009-09-09 03:05:48
P18
300000
6796 2009-09-09 03:38:40
P19
300000
6796 2009-09-09 04:10:07
P20
300000
6796 2009-09-09 04:14:57
P21
300000
6796 2009-09-09 04:46:03
P22
300000
6796 2009-09-09 04:57:09
P23
300000
6796 2009-09-09 05:28:45
P24
300000
6796 2009-09-11 22:03:14
P25
300000
6796 2009-09-11 00:31:14
P26
300000
6796 2009-09-11 00:37:14
P27
300000
6796 2009-09-11 00:44:13
P28
300000
6796 2009-09-11 00:52:49
P29
300000
6796 2009-09-11 01:00:16
P30
300000
6796 2009-09-11 01:06:34
PMAX
0
0 2009-09-11 01:06:34

2.6 The drawback or bugs of incremental statistics:


2.6.1: It is only sensitive for newly load data partition:

Per my test, it seems that, incremental statistics is only sensitive for a new partition which is
completely newly loaded data. It can be aware of the following situations:
a. delete/insert some data to a partition that has already been analyzed
b. truncate one/more partitions
Please see my test:
The table currently has 1.8 million records, each partition contain 0.3 million records:
SQL> select partition_name, num_rows, blocks, last_analyzed from
user_tab_partitions where table_name = 'TEST_INC' and num_rows > 0;
PARTITION_NAME
NUM_ROWS
BLOCKS LAST_ANALYZED
------------------------------ ---------- ---------- ------------------P00
300000
6796 2009-09-12 01:41:56
P01
300000
6796 2009-09-12 01:45:07
P02
300000
6796 2009-09-12 01:47:11
P03
300000
6796 2009-09-12 01:50:11
P04
300000
6796 2009-09-12 01:51:41
P05
300000
6796 2009-09-12 01:52:48

Delete 100k records for partition p05:


SQL> delete from test_inc partition(p05) where rownum <= 100000;
100000 rows deleted.
Truncate partition p04:
SQL> alter table test_inc truncate partition p04 ;
Table truncated.
Insert 100k records to partition p01:
SQL> insert into test_inc select rownum, sysdate+mod(rownum, 30),
'wangweifengwangweifeng'||rownum,
2 rpad('x', 50, 'x')||rownum, rpad('y', 50, 'z')||rownum, 1 from dual
connect by rownum <= 300000;
300000 rows created.

But, let us analyze it:


SQL> exec dbms_stats.gather_table_stats(user, 'test_inc');
PL/SQL procedure successfully completed.

SQL> select table_name, num_rows, blocks, last_analyzed from


user_tables where table_name = 'TEST_INC';
TABLE_NAME
NUM_ROWS
BLOCKS LAST_ANALYZED
------------------------------ ---------- ---------- ------------------TEST_INC
1800000
41361 2009-09-12 01:57:21

SQL> select partition_name, num_rows, blocks, last_analyzed from


user_tab_partitions where table_name = 'TEST_INC' and num_rows > 0;
PARTITION_NAME
NUM_ROWS
BLOCKS LAST_ANALYZED
------------------------------ ---------- ---------- ------------------P00
300000
6796 2009-09-12 01:41:56
P01
300000
6796 2009-09-12 01:45:07
P02
300000
6796 2009-09-12 01:47:11

P03
P04
P05

300000
300000
300000

6796 2009-09-12 01:50:11


6796 2009-09-12 01:51:41
6796 2009-09-12 01:52:48

6 rows selected.
SQL> select count(*) from test_inc partition(p05);
COUNT(*)
---------200000
SQL> select count(*) from test_inc partition(p06);
COUNT(*)
---------0
SQL> select count(*) from test_inc partition(p01);
COUNT(*)
---------600000
We can see, the statistics does not change at all!
Then, will it triggered by populating data into a newly loaded partition?
It won
wont! Let me insert data to an empty partition p06:
SQL> insert into test_inc select rownum, sysdate+mod(rownum, 30),
'wangweifengwangweifeng'||rownum,
2 rpad('x', 50, 'x')||rownum, rpad('y', 50, 'z')||rownum, 6 from
dual connect by rownum <= 300000;
300000 rows created.
SQL> exec dbms_stats.gather_table_stats(user, 'test_inc');
PL/SQL procedure successfully completed.
Elapsed: 00:00:06.61
SQL> select table_name, num_rows, blocks, last_analyzed from
user_tables where table_name = 'TEST_INC';
TABLE_NAME
NUM_ROWS
BLOCKS LAST_ANALYZED
------------------------------ ---------- ---------- ------------------TEST_INC
2100000
48157 2009-09-12 02:01:36

SQL> select partition_name, num_rows, blocks, last_analyzed from


user_tab_partitions where table_name = 'TEST_INC' and num_rows > 0;
PARTITION_NAME
NUM_ROWS
BLOCKS LAST_ANALYZED
------------------------------ ---------- ---------- ------------------P00
300000
6796 2009-09-12 01:41:56
P01
300000
6796 2009-09-12 01:45:07
P02
300000
6796 2009-09-12 01:47:11
P03
300000
6796 2009-09-12 01:50:11
P04
300000
6796 2009-09-12 01:51:41
P05
300000
6796 2009-09-12 01:52:48

P06

300000

6796 2009-09-12 02:01:33

7 rows selected.

How about I truncate all tables?


Let me initialize the table and populate 2.1 million records for 7 partitions:
SQL> select table_name, num_rows, blocks, last_analyzed from
user_tables where table_name = 'TEST_INC';
TABLE_NAME
NUM_ROWS
BLOCKS LAST_ANALYZED
------------------------------ ---------- ---------- ------------------TEST_INC
2100000
47572 2009-09-12 04:05:18

SQL> select partition_name, num_rows, blocks, last_analyzed from


user_tab_partitions where table_name = 'TEST_INC' and num_rows > 0;
PARTITION_NAME
NUM_ROWS
BLOCKS LAST_ANALYZED
------------------------------ ---------- ---------- ------------------P00
300000
6796 2009-09-12 03:53:58
P01
300000
6796 2009-09-12 03:54:42
P02
300000
6796 2009-09-12 03:55:24
P03
300000
6796 2009-09-12 04:01:54
P04
300000
6796 2009-09-12 04:03:33
P05
300000
6796 2009-09-12 04:04:26
P06
300000
6796 2009-09-12 04:05:16

7 rows selected.
SQL> truncate table test_inc;
Table truncated.
Elapsed: 00:00:02.24
SQL> exec dbms_stats.gather_table_stats(user, 'test_inc');
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.61
SQL> select table_name, num_rows, blocks, last_analyzed from
user_tables where table_name = 'TEST_INC';
TABLE_NAME
NUM_ROWS
BLOCKS LAST_ANALYZED
------------------------------ ---------- ---------- ------------------TEST_INC
0
0 2009-09-12 04:09:43

SQL> select partition_name, num_rows, blocks, last_analyzed from


user_tab_partitions where table_name = 'TEST_INC' and num_rows > 0;
PARTITION_NAME
NUM_ROWS
BLOCKS LAST_ANALYZED
------------------------------ ---------- ---------- ------------------P00
300000
6796 2009-09-12 03:53:58
P01
300000
6796 2009-09-12 03:54:42
P02
300000
6796 2009-09-12 03:55:24
P03
300000
6796 2009-09-12 04:01:54
P04
300000
6796 2009-09-12 04:03:33
P05
300000
6796 2009-09-12 04:04:26
300000
6796 2009-09-12 04:05:16
P06
7 rows selected.

We can see: the statistics for table level is got updated, but, it is not got updated for
partition level!
I think, it should be a bug! Anyway, I will file TAR for Oracle for clarify.
, then analyze again:
How to fix it temporarily? Set it back to no-incremental
no-incremental
SQL> exec
'FALSE'
DBMS_STATS.SET_TABLE_PREFS(user,'test_inc','INCREMENTAL','FALSE'
'FALSE');
PL/SQL procedure successfully completed.
SQL> select DBMS_STATS.get_PREFS('incremental', 'wwf', 'test_inc') from
dual;
DBMS_STATS.GET_PREFS('INCREMENTAL','WWF','TEST_INC')
----------------------------------------------------------------------FALSE
SQL> exec dbms_stats.gather_table_stats(user, 'test_inc');
PL/SQL procedure successfully completed.
SQL> select partition_name, num_rows, blocks, last_analyzed from
user_tab_partitions where table_name = 'TEST_INC' and num_rows > 0;
no rows selected
SQL> select table_name, num_rows, blocks, last_analyzed from
user_tables where table_name = 'TEST_INC';
TABLE_NAME
NUM_ROWS
BLOCKS LAST_ANALYZED
------------------------------ ---------- ---------- ------------------TEST_INC
0
0 2009-09-12 04:18:34

SQL> select partition_name, num_rows, blocks, last_analyzed from


user_tab_partitions where table_name = 'TEST_INC';
PARTITION_NAME
NUM_ROWS
BLOCKS LAST_ANALYZED
------------------------------ ---------- ---------- ------------------P00
0
0 2009-09-12 04:18:34
P01
0
0 2009-09-12 04:18:34
P02
0
0 2009-09-12 04:18:34
P03
0
0 2009-09-12 04:18:34
P04
0
0 2009-09-12 04:18:34
P05
0
0 2009-09-12 04:18:34
P06
0
0 2009-09-12 04:18:34
P07
0
0 2009-09-12 04:18:34

2.6.2 For subpartition table, it is totally wrong:


SQL>
2
3
4
5
6
7
8
9

CREATE TABLE composite_rng_list (


cust_id
NUMBER(10),
cust_name
VARCHAR2(25),
cust_state VARCHAR2(2),
time_id
DATE)
PARTITION BY RANGE(time_id)
SUBPARTITION BY LIST (cust_state)
SUBPARTITION TEMPLATE(
SUBPARTITION west VALUES ('OR', 'WA'),

10
11
12
13
14
15

SUBPARTITION east VALUES ('NY', 'CT'),


SUBPARTITION cent VALUES ('OK', 'TX')) (
PARTITION per1 VALUES LESS THAN (TO_DATE('01/01/2008','DD/MM/YYYY')),
PARTITION per2 VALUES LESS THAN (TO_DATE('01/01/2010','DD/MM/YYYY')),
PARTITION per3 VALUES LESS THAN (TO_DATE('01/01/2012','DD/MM/YYYY')),
PARTITION future VALUES LESS THAN(MAXVALUE));

Table created.

SQL> exec dbms_stats.set_table_prefs('wwf', 'composite_rng_list',


'incremental', 'true')
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.01
SQL> select dbms_stats.get_prefs('incremental', 'wwf',
'composite_rng_list') from dual;
DBMS_STATS.GET_PREFS('INCREMENTAL','WWF','COMPOSITE_RNG_LIST')
--------------------------------------------------------------------TRUE
SQL> insert into composite_rng_list select rownum, 'customer'||rownum, 'OR',
to_date('2007-01-01', 'yyyy-mm-dd') from dual connect by rownum <= 100000;

100000 rows created.


SQL> commit;
Commit complete.
SQL> select table_name, num_rows, blocks, last_analyzed from user_tables where
table_name = 'COMPOSITE_RNG_LIST';
TABLE_NAME
NUM_ROWS
BLOCKS LAST_ANALYZED
------------------------------ ---------- ---------- ------------------COMPOSITE_RNG_LIST
100000
496 2009-09-14 18:17:58

Elapsed: 00:00:00.01
SQL> select partition_name, num_rows, blocks, last_analyzed from
user_tab_partitions where table_name = 'COMPOSITE_RNG_LIST' and rownum > 0;
PARTITION_NAME
NUM_ROWS
BLOCKS LAST_ANALYZED
---------------- ---------- ---------- ------------------PER1
100000
496
2009-09-14 18:17:57
Elapsed: 00:00:00.00
SQL> select partition_name, SUBPARTITION_NAME, num_rows, blocks, last_analyzed
from user_tab_subpartitions where table_name = 'COMPOSITE_RNG_LIST' and rownum
> 0;
PARTITION_NAME
SUBPARTITION_NAME NUM_ROWS
---------------- ---------------- ---------PER1
PER1_WEST
100000

BLOCKS LAST_ANALYZED
---------- ------------------496 2009-09-14 18:17:55

Then, insert 100000 more rows and analyze again, we can see that, the
statistics at partition level and table level were not changed at all:
SQL> insert into composite_rng_list select rownum, 'customer'||rownum, 'WA',
to_date('2007-01-01', 'yyyy-mm-dd') from dual connect by rownum <= 100000;

100000 rows created.


SQL> commit;
Commit complete.
SQL> exec dbms_stats.gather_table_stats(user, 'COMPOSITE_RNG_LIST',
granularity=>'ALL')
PL/SQL procedure successfully completed.
SQL> select table_name, num_rows, blocks, last_analyzed from user_tables where
table_name = 'COMPOSITE_RNG_LIST';
TABLE_NAME
NUM_ROWS
BLOCKS LAST_ANALYZED
------------------------------ ---------- ---------- ------------------COMPOSITE_RNG_LIST
100000
1000 2009-09-14 18:24:12
SQL> select partition_name, num_rows, blocks, last_analyzed from
user_tab_partitions where table_name = 'COMPOSITE_RNG_LIST' and num_rows > 0;
PARTITION_NAME
NUM_ROWS
BLOCKS LAST_ANALYZED
------------------------------ ---------- ---------- ------------------PER1
100000
496 2009-09-14 18:17:57
SQL> select partition_name, SUBPARTITION_NAME, num_rows, blocks, last_analyzed
from user_tab_subpartitions where table_name = 'COMPOSITE_RNG_LIST' and
num_rows > 0;
PARTITION_NAME SUBPARTITION_NAME
--------------- -------------PER1
PER1_WEST

NUM_ROWS
BLOCKS
LAST_ANALYZED
---------- ---------- ------------------200000
1000
2009-09-14 18:24:11

Then, insert 100000 more rows and analyze again, we can see that, the
statistics at partition level and table level were not changed at all:
SQL> insert into composite_rng_list select rownum, 'customer'||rownum,
'NY', to_date('2007-01-01', 'yyyy-mm-dd') from dual connect by rownum <=
100000;
100000 rows created.
SQL> commit;
Commit complete.
SQL> exec dbms_stats.gather_table_stats(user, 'COMPOSITE_RNG_LIST',
granularity=>'ALL')
PL/SQL procedure successfully completed.
SQL> select table_name, num_rows, blocks, last_analyzed from user_tables where
table_name = 'COMPOSITE_RNG_LIST';
TABLE_NAME
NUM_ROWS
BLOCKS LAST_ANALYZED
------------------------------ ---------- ---------- ------------------COMPOSITE_RNG_LIST
100000
1496 2009-09-14 18:26:33
SQL> select partition_name, num_rows, blocks, last_analyzed from
user_tab_partitions where table_name = 'COMPOSITE_RNG_LIST' and num_rows > 0;
PARTITION_NAME
NUM_ROWS
BLOCKS
LAST_ANALYZED
----------------- ---------- ---------- ------------------PER1
100000
496
2009-09-14 18:17:57

SQL> select partition_name, SUBPARTITION_NAME, num_rows, blocks, last_analyzed


from user_tab_subpartitions where table_name = 'COMPOSITE_RNG_LIST' and
num_rows > 0;
PARTITION_NAME SUBPARTITION_NAME NUM_ROWS
BLOCKS
------------ ----------------- ---------- ---------PER1
PER1_WEST
200000
1000
PER1
PER1_EAST
100000
496

LAST_ANALYZED
------------------2009-09-14 18:26:32
2009-09-14 18:26:32

Also, I do more test on subpartitione table. It can not display


accurate statistics on partition level and table level.
2.6.3 There is no incremental statistics for partitioned indexes on partitioned tables:
On the partitioned table in 2.1, we create a local partitioned index:
SQL> create index ind_test_inc on test_inc(a) local;
Index created.
Insert 300k rows, then analyze:
SQL> select partition_name, num_rows, DISTINCT_KEYS, clustering_factor,
last_analyzed from user_ind_partitions where index_name = 'IND_TEST_INC' and
num_rows > 0;
PARTITION_NAME
--------------P00

NUM_ROWS DISTINCT_KEYS CLUSTERING_FACTOR LAST_ANALYZED


--------- ------------- ----------------- -----------------300000
300000
6763 2009-09-14 19:43:01

Insert 300k rows, then analyze:


SQL> select partition_name, num_rows, DISTINCT_KEYS, clustering_factor,
last_analyzed from user_ind_partitions where index_name = 'IND_TEST_INC' and
num_rows > 0;
PARTITION_NAME NUM_ROWS DISTINCT_KEYS

CLUSTERING LAST_ANALYZED
FACTOR
------------- ---------- ------------- ----------------- -------------P00
300000
300000
6763
2009-09-14 19:54:35
P01
300000
300000
6763
2009-09-14 19:54:35

Insert 300k rows, then, analyze:


SQL> select num_rows, DISTINCT_KEYS, clustering_factor, last_analyzed from
user_indexes where index_name = 'IND_TEST_INC';
NUM_ROWS DISTINCT_KEYS CLUSTERING_FACTOR LAST_ANALYZED
---------- ------------- ----------------- ------------------900000
302976
20289 2009-09-14 20:00:50
SQL> select partition_name, num_rows, DISTINCT_KEYS, clustering_factor,
last_analyzed from user_ind_partitions where index_name = 'IND_TEST_INC' and
num_rows > 0;
PARTITION_NAME NUM_ROWS DISTINCT_KEYS CLUSTERING_FACTOR LAST_ANALYZED
--------------- ---------- ------------- ----------------- ------------------P00
300000
300000
6763 2009-09-14 20:00:50
P01
300000
300000
6763 2009-09-14 20:00:50
P02
300000
300000
6763 2009-09-14 20:00:50

as incremental:
It seems that there is no way to set index ind_test_inc
ind_test_inc
SQL> select dbms_stats.get_prefs('incremental', 'wwf', 'test_inc') from dual;
DBMS_STATS.GET_PREFS('INCREMENTAL','WWF','TEST_INC')

-------------------------------------------TRUE
Elapsed: 00:00:00.01
SQL> select dbms_stats.get_prefs('incremental', 'wwf', 'ind_test_inc'
'ind_test_inc') from
dual;
DBMS_STATS.GET_PREFS('INCREMENTAL','WWF','IND_TEST_INC')
----------------------------------------------FALSE
Elapsed: 00:00:00.01
SQL> exec dbms_stats.set_table_prefs('wwf', 'ind_test_inc'
'ind_test_inc', 'incremental',
'true')
BEGIN dbms_stats.set_table_prefs('wwf', 'ind_test_inc', 'incremental', 'true');
END;
*
ERROR at line 1:
ORA-20000: TABLE "WWF"."IND_TEST_INC" does not exist or insufficient privileges
ORA-06512: at "SYS.DBMS_STATS", line 2091
ORA-06512: at "SYS.DBMS_STATS", line 24110
ORA-06512: at line 1

I will do tests on 11G Release 2 once it is available for download on Solaris version.
But, from the document of 11G Release 2, which has been already available, there is still
no such function in such as set_index_prefs provided.

3. Under the hood


3.1 Where the synopsis exists?
As it is said in part 1, it is in sysaux tablespace:
SQL> select OCCUPANT_NAME, OCCUPANT_DESC, SPACE_USAGE_KBYTES/power(1024,2) from
v$sysaux_occupants order by 2;
OCCUPANT_NAME
OCCUPANT_DESC
SPAC
E_USAGE_KBYTES/POWER(1024,2)
--------------------- ---------------------------------------------------- ------------------------------AO
Analytical Workspace Object
Table
.001342773
AUTO_TASK
Automated Maintenance
Tasks
.000305176
EM_MONITORING_USER
Enterprise Manager Monitoring
User
.001647949
LOGMNR
LogMiner
.007385254
LOGSTDBY
Logical
Standby
.000976563
XSOQHIST
OLAP API History
Tables
.001342773
STREAMS
Oracle
Streams
.000976563
TSM
Oracle Transparent Session Migration
User
.000244141
PL/SCOPE
PL/SQL Identifier
Collection
.000366211
SQL_MANAGEMENT_BASE
SQL Management Base
Schema
.001647949

SM/ADVISOR
Framework
SM/AWR
Repository
SM/OPTSTAT
History
SM/OTHER
Components
SMON_SCN_TIME
mapping
JOB_SCHEDULER
Scheduler

Server Manageability - Advisor


.016052246
Server Manageability - Automatic Workload
.125610352
Server Manageability - Optimizer Statistics
.115722656
Server Manageability - Other
.00567627
Transaction Layer - SCN to TIME
.003173828
Unified Job
.000610352

29 rows selected.

The blue line represents the space occupied by the synopsis for global level statistics.
Usually, it will only consume very little space.
Using sql_trace, found that the attribute for incremental for a table is stored in table
SYS.OPTSTAT_USER_PREF$:
SQL> select a.obj#, name, pname, valchar from SYS.OPTSTAT_USER_PREFS$ a, obj$ b
where a.obj# = b.obj#;
OBJ# NAME
PNAME
VALCHAR
---------- ------------------------------ -------------------- ---------------15307 TEST_INC
INCREMENTAL
TRUE

(Number of Distinct Value) of


In fact, for a partitioned table, the global level NDV
NDV
columns is the most challengeable. All other statistics can be speculated from partition
level, such as num_rows, blocks, avg_row_len. So, I think, this is what Oracle 11G Release
2 enhances.

4. My conclusion:
I think, incremental statistics gathering for partitioned tables is a very useful new feature
introduced by ORACLE 11G if all the bugs can be resolved.
It will consume much less resource especially for large partitioned/subpartitioned tables
and shorten our statistics gathering process. I believe it will
not only help us for our normal analyze CR, but also it will help us under situations under
tough situations (such as TCB) caused by inaccurate statistics,
as at such situations, time is so precious for us.
Anyway, I will track this feature.
Thanks!

Você também pode gostar