由在EBS中收集分区表统计信息引发的思考

今天在看EBS性能调整文档的时候,有这样一段关于分区表统计数据收集的描述:
When using the 'Gather Table Statistics' concurrent program, pass only the owner of the table (schema name) and the table name. Let all other parameters default automatically, except when the table has been partitioned. When gathering CBO stats for a partitioned table, pass 'PARTITION' in the Granularity parameter, otherwise FND_STATS will calculate global stats (plus partition stats) instead of rolling up the global stats from the partitions. If this happens, you may have to delete the global stats(with cascade equals 'false') and gather the stats for one partition to once again enable the automatic rolling up into the global stats.
FND_STATS是基于DBMS_STATS的。为啥分区表的统计数据需要用分区的粒度去收集呢?直接收集global信息不行吗?搜索了一下DBMS_STATS的资料,找到一篇文章证实了我想法。

什么是global statistics?

大家都知道,dbms_stats是Oracle 9i及后续的版本中用于收集优化器统计信息的包,虽然analyze命令也一直可用,但是现在已经不推荐使用analyze来收集统计信息,而是使用dbms_stats。二者之间一个很大的不同,也是dbms_stats一个很突出的优点就是能够正确收集分区表的统计信息,换言之就是global statistics。而analyze命令只会收集最低层次对象的统计信息,然后推导和汇总出高一级的统计信息,如分区表只会收集分区信息,然后再汇总所有分区的统计信息,得到表一级的统计信息。

那什么是global statistics?简单地说global statistics就是指直接从对象本身这一级收集到的统计信息,而不是从下一级对象“推导”或“汇总”出来的统计信息。比如,表的global statistics指直接通过表收集到的统计信息,而不是从分区收集的统计信息进行汇总或推导出的。同样,分区的global statistics是指直接通过分区收集到的统计信息,而不是从子分区收集的统计信息进行汇总或推导出的。global statistics对优化器来说是非常重要的,一个SQL,除非其查询条件限定了数据只在部分分区上,否则在大多数情况下需要global statistics才能得到正确的执行计划。

有的统计值可以从对象的下一级对象进行汇总后得到,比如表的总行数,可以通过各分区的行数相加得到。但有的统计值则不能从下一级对象得到,比如列上的唯一值数量(distinct value)以及密度值(density)。

怎样收集global statistics?

global statistics只能通过dbms_stats包来收集。注意,用analyze命令得到的统计信息,虽然也会有表一级的统计值,但是,那些值是从分区或子分区推导和汇总出来的,是不精确的。后面的实验中,将会验证这一点。

使用dbms_stats收集统计信息时,参数granularity(比如gather_table_stats过程) 指定了哪个级别上的统计信息会被收集:

  • Global -- Table (表)
  • Partition -- Partition (分区)
  • SubPartition -- SubPartition (子分区)
  • Default -- Table + Partition (表+分区)
  • All -- Table + Partition + Subpartition (表+分区+子分区)
  • Auto -- Table + Partition + Subpartition (10g,表+分区,当子分区是range和list分区时还包括子分区)

比如,要在一个做为子分区的表上,以’ALL’级别收集统计信息时,会收集”表+分区+子分区“上的统计信息,相当于需要执行下面的三类SQL:

  • select …. from table
  • select …. from table (partition P1) -- 在每个分区上都重复一次
  • select …. from table (subpartition SP1) -- 在每个子分区上重复一次

可以看到,dbms_stats需要比analyze命令更多的时间来进行统计信息的收集。对于有子分区的表,dbms_stats至少要多花三倍的时间。

怎样查看表是否收集了global statistics?

统计信息(下面主要描述表和分区,不涉及索引)一般是通过查看下列视图来检查的:

  • 表的统计信息:

    [DBA|ALL|USER]_TABLES
    [DBA|ALL|USER]_TAB_COL_STATISTICS

  • 分区的统计信息:

    [DBA|ALL|USER]_TAB_PARTITIONS
    [DBA|ALL|USER]_PART_COL_STATISTICS

  • 子分区的统计信息:

    [DBA|ALL|USER]_TAB_SUBPARTITIONS
    [DBA|ALL|USER]_SUBPART_COL_STATISTICS

在以上的视图中,如果GLOBAL_STATS列值为”YES“,表示该级对象上收集了global statistics,而值为”NO“则表示没有收集global statistics。

下面用一些实验来更深入地验证dbms_stats,analyze以及global statistics三者之间的联系。测试环境为Linux AS4上的Oracle 10.2.0.3。注意不同的Oracle可能有不同的行为。

我们先用下面的SQL创建一个测试表,这个表有2个分区,每个分区有2个子分区:

CREATE TABLE T3(i number, p number,sp number)
PARTITION BY RANGE(p)
SUBPARTITION BY HASH(sp) SUBPARTITIONS 2
(
PARTITION q1 VALUES LESS THAN(3) TABLESPACE USERS,
PARTITION q2 VALUES LESS THAN(MAXVALUE) TABLESPACE USERS
);

declare
i number;
begin
for i in 1..100000 loop
insert into T3 values(i,mod(i,7), mod(i,8));
if( mod(i, 1000) = 0) then commit; end if;
end loop;
for i in 1..50000 loop
insert into T3 values(i,mod(i,7), mod(i,8)+5);
if( mod(i, 1000) = 0) then commit; end if;
end loop;
end;
/

我们向测试表中插入15W行数据。I列有100,000个不同的值,P列有7个不同的值,SP列有13个不同的值。为了能够方便地查看收集的统计信息,我们使用了一段来自Metalink的sql代码,首先用@sosi可以看到,T3没有任何统计信息存在。
使用analyze table t3 compute statistics;收集T3表的统计信息,可以看到如下的统计信息(由于版面原因,去除了一些与本文无关的信息):

SQL> analyze table t3 compute statistics;
SQL> @sosi
SQL> set echo off

Tables owned by TEST
------------------------------
T3

Please enter Name of Table Owner (Null = TEST):
Please enter Table Name to show Statistics for: T3

***********
Table Level
***********

Table Number Empty Average Chain Average Global User Sample
Name of Rows Blocks Blocks Space Count Row Len Stats Stats Size
------------- -------- ------ ------- ----- ------- ------ ------ ------
T3 150,000 328 32 921 0 13 NO NO 0

Column Column Distinct Number Number Global User Sample
Name Details Values Density Buckets Nulls Stats Stats Size
------ ------------------ ------- ------- ------ ------ ------ ------
I NUMBER(22) 59,845 0 1 0 NO NO
P NUMBER(22) 7 0 1 0 NO NO
SP NUMBER(22) 8 0 1 0 NO NO

***************
Partition Level
***************
Partition Number Empty Average Chain Average Global User Sample
Name of Rows Blocks Blocks Space Count Row Len Stats Stats Size
--------- ------- -------- ------ ------- ----- ------- ------ ------ ------
Q1 64,285 138 14 878 0 13 NO NO
Q2 85,715 190 18 954 0 14 NO NO

Partition Column Distinct Number Number Global User Sample
Name Name Values Density Buckets Nulls Stats Stats Size
--------- ------ -------- ------- ------- ------ ------ ------ ------
Q1 I 44,878 0 1 0 NO NO
P 3 0 1 0 NO NO
SP 8 0 1 0 NO NO
Q2 I 59,844 0 1 0 NO NO
P 4 0 1 0 NO NO
SP 8 0 1 0 NO NO
***************
SubPartition Level
***************

Partition SubPartition Number Empty Average Average Global User Sample
Name Name of Rows Blocks Blocks Space Row Len Stats Stats Size
--------- ------------ ------- -------- ------ ------- ------- ------ ------ ------
Q2 SYS_SUBP31 35,715 80 8 992 14 NO NO 35,715
Q1 SYS_SUBP29 26,785 58 6 902 14 NO NO 26,785
Q2 SYS_SUBP32 50,000 110 10 928 14 NO NO 50,000
Q1 SYS_SUBP30 37,500 80 8 861 13 NO NO 37,500

Partition SubPartition Column Distinct Number Number Global User Sample
Name Name Name Values Density Buckets Nulls Stats Stats Size
--------- --------------- ------- -------- ------- ------- ------ ------ ------ ------
Q1 SYS_SUBP29 I 24,106 0 1 0 NO NO 26,785
SYS_SUBP29 P 3 0 1 0 NO NO 26,785
SYS_SUBP29 SP 5 0 1 0 NO NO 26,785
SYS_SUBP30 I 32,142 0 1 0 NO NO 37,500
SYS_SUBP30 P 3 0 1 0 NO NO 37,500
SYS_SUBP30 SP 8 0 1 0 NO NO 37,500
Q2 SYS_SUBP31 I 32,144 0 1 0 NO NO 35,715
SYS_SUBP31 P 4 0 1 0 NO NO 35,715
SYS_SUBP31 SP 5 0 1 0 NO NO 35,715
SYS_SUBP32 I 42,858 0 1 0 NO NO 50,000
SYS_SUBP32 P 4 0 1 0 NO NO 50,000
SYS_SUBP32 SP 8 0 1 0 NO NO 50,000

由上面的表可以看到,analyze只是收集了sub partition的统计信息,然后经过汇总和推导得到了partition和table级别的统计信息。global_stats列值均为”NO”。在表级的I字段的统计信息上看到,I的唯一值个数为59,845,而实际的值应该为100,000,这里差距是比较大的;SP的唯一值个数为8,而实际的值应该为13。

下面再将统计信息删除,然后使用dbms_stats重新收集统计信息:

SQL> exec dbms_stats.gather_table_stats(ownname=>user,tabname=>’T3′,granularity=>’ALL’);
SQL> @sosi
SQL> set echo off

Tables owned by TEST
------------------------------
T3

Please enter Name of Table Owner (Null = TEST):
Please enter Table Name to show Statistics for: T3

***********
Table Level
***********

Table Number Empty Average Chain Average Global User Sample
Name of Rows Blocks Blocks Space Count Row Len Stats Stats Size
------ ------- -------- ------ ------- ----- ------- ------ ------ -------
T3 150,000 328 0 0 0 10 YES NO 150,000

Column Column Distinct Number Number Global User Sample Date
Name Details Values Density Buckets Nulls Stats Stats Size MM-DD-YYYY
------ ----------- -------- ------- ------- ------ ------ ------ ------- ----------
I NUMBER(22) 100,000 0 1 0 YES NO 150,000 12-15-2008
P NUMBER(22) 7 0 1 0 YES NO 5,497 12-15-2008
SP NUMBER(22) 13 0 1 0 YES NO 5,497 12-15-2008
***************
Partition Level
***************

Partition Number Empty Average Chain Average Global User Sample
Name of Rows Blocks Blocks Space Count Row Len Stats Stats Size
--------- ------- -------- ------ ------- ----- ------- ------ ------ ------
Q1 64,285 138 0 0 0 10 YES NO 64,285
Q2 85,715 190 0 0 0 10 YES NO 85,715

Partition Column Distinct Number Number Global User Sample
Name Name Values Density Buckets Nulls Stats Stats Size
--------- ------- -------- ------- ------- ------ ------ ------ ------
Q1 I 42,857 0 1 0 YES NO 64,285
P 3 0 1 0 YES NO 5,584
SP 13 0 1 0 YES NO 5,584
Q2 I 57,143 0 1 0 YES NO 85,715
P 4 0 1 0 YES NO 5,443
SP 13 0 1 0 YES NO 5,443

***************
SubPartition Level
***************

Partition SubPartition Number Empty Average Average Global User Sample
Name Name of Rows Blocks Blocks Space Row Len Stats Stats Size
--------- ------------- ------- -------- ------ ------- ------- ------ ----- ------
Q2 SYS_SUBP31 35,715 80 0 0 10 YES NO 35,715
Q1 SYS_SUBP29 26,595 58 0 0 10 YES NO 5,461
Q2 SYS_SUBP32 50,000 110 0 0 10 YES NO 50,000
Q1 SYS_SUBP30 37,500 80 0 0 10 YES NO 37,500

Partition SubPartition Column Distinct Number Number Global User Sample
Name Name Name Values Density Buckets Nulls Stats Stats Size
---------- --------------- ------- -------- ------- ------- ------ ------ ------ ------
Q1 SYS_SUBP29 I 26,084 0 1 0 YES NO 5,461
SYS_SUBP29 P 3 0 1 0 YES NO 5,461
SYS_SUBP29 SP 5 0 1 0 YES NO 5,461
SYS_SUBP30 I 32,142 0 1 0 YES NO 37,500
SYS_SUBP30 P 3 0 1 0 YES NO 5,459
SYS_SUBP30 SP 8 0 1 0 YES NO 5,459
Q2 SYS_SUBP31 I 32,144 0 1 0 YES NO 35,715
SYS_SUBP31 P 4 0 1 0 YES NO 5,605
SYS_SUBP31 SP 5 0 1 0 YES NO 5,605
SYS_SUBP32 I 42,858 0 1 0 YES NO 50,000
SYS_SUBP32 P 4 0 1 0 YES NO 5,500
SYS_SUBP32 SP 8 0 1 0 YES NO 5,500

从上面的统计信息可以看到,不管是在partition级,还是在table级,I列和SP列的唯一值个数都得到了正确的值。在三种级别(table,partition,subpartition)上,global_stats列值都为“YES”,表示三个级别收集的都是global statistics。

将统计信息删除,使用exec dbms_stats.gather_table_stats(ownname=>user,tabname=>’T3′,granularity=>’SUBPARTITION’);
收集子分区级别上的统计信息(此处不再列出具体的统计信息数据),则子分区统计信息中的global_stats值为”YES”,而分区和表级统计信息中的global_stats值为”NO”,这两级的统计数据则是由子分区级统计信息汇总和推导得出。
而使用exec dbms_stats.gather_table_stats(ownname=>user,tabname=>’T3′,granularity=>’PARTITION’);收集分区级上的统计信息,则子分区没有统计信息,表级的统计信息也是由分区级统计信息推导和汇总而来。
而在缺省情况下,即granularity参数为default值时,分区和表都有global statistics,而子分区级没有任何统计信息 。

在以上的测试中,在进行新的统计信息收集之前,都是先删除了原来的统计信息。但是如果我们在收集统计信息之前没有删除原来的统计数据,并且不同的统计策略,会有不同的影响。但大部分都是很不好的影响,容易造成统计信息的不准确。比如:

  • 使用dbms_stats以granularity=’ALL’收集统计信息,删除一部分数据之后,再以更低级别的粒度收集统计数据:

SQL> exec dbms_stats.gather_table_stats(ownname=>user,tabname=>’T3′,granularity=>’ALL’);
SQL> delete from t3 where i<3000;
SQL> commit;
SQL> exec dbms_stats.gather_table_stats(ownname=>user,tabname=>’T3′,granularity=>’PARTITION’);
SQL>

然后对比统计信息,可以发现,表级和子分区级仍然保留着原来的过时的统计信息。综合其他的几个测试(此处不再列出),我们可以得到这个结论:使用dbms_stats收集统计信息,粒度(granularity)为较低级别时,更低级别的统计信息保持不变,而高级别的统计信息由收集的这一级别的统计信息推导和汇总得出,但不能跨级别推导和汇总。但如果高级别的统计信息之前是global statistics,则那一级别的统计信息保持不变。analyze始终是在子分区级别收集统计信息,其高级别统计信息的推导和汇总的行为与dbms_stats一样。比如,现在表级的统计信息global_stats为NO,分区级的统计信息global_stats为YES,使用SUBPARTITION级别或使用analyze命令分析后,分区级的统计信息仍然是原来的统计信息,而表级的统计信息其global_stats为NO,但由于不能跨级推导和汇总,所以表这一级的统计信息仍然是原来的统计信息。

  • 使用dbms_stats以granularity=’PARTITION’收集统计信息,然后使用analyze删除统计信息:

SQL> exec dbms_stats.gather_table_stats(ownname=>user,tabname=>’T3′,granularity=>’PARTITION’);
SQL> analyze table t3 delete statistics;
SQL> exec dbms_stats.gather_table_stats(ownname=>user,tabname=>’T3′,granularity=>’ALL’);
SQL> analyze table t3 delete statistics;

执行上面的命令序列之后,我们可以发现,analyze ..delete statistics命令总是会删除子分区的统计信息,但分区级和表级,如果有global statistics统计信息,则不会删除。并且不能跨级删除,比如分区有global statistics,由表级没有global statistics,表级的统计信息也不会被删除。综合其他几个测试,也可以得到:如果某一级(partition,subpartition)上有统计信息,那么比它更高的所有级别上都会有统计信息。

  • 各种情况下使用dbms_stats.delete_table_stats删除统计信息,可以发现:

dbms_stats.delete_table_stats总是可以删除表上所有的三个级别的统计信息。

  • 使用analyze命令收集的统计信息,有如下特点:

    analyze命令收集的统计信息,global_stats总是为”NO“。

    综合以上测试和得到的结果,总结出我们在收集优化统计信息需要注意以下几个方面:

  • 分区表的统计信息使用dbms_stats包中的过程,这样才能收集global statistics。
  • 删除表的统计信息使用dbms_stats包中的过程,analyze命令删除统计信息,可能不是你想要的结果,因为有可能部分统计信息并没有被删除。
  • 收集统计信息时,使用一致的granularity参数,比如开始用ALL,过一段时间用DEFAULT或PARTITION,这样得出的结果,在某些级别上的统计信息是过时的,不准确的。
  • 由于analyze命令只在最低一级收集统计信息,而高一级的统计信息是下级统计信息汇总和推导而来。因此不要将analyze命令与dbms_stats混合使用于同一张表上,其原因如同上一条。

本文涉及到的内容,只有表(并且是普通的HEAP表),不包括索引。也没有涉及到直方图。本文部分内容来源于Metalink Note ID:236935.1 ”Global statistics - An Explanation“

如果需要深入研究analyze和dbms_stats,感兴趣的可以使用sql trace或10046事件来跟踪二者的行为。

文章地址:http://www.laoxiong.net/dbms_stats_and_analyze_and_global_statistics.html

sosi.sql代码如下:

set echo off
set scan on
set lines 150
set pages 66
set verify off
set feedback off
set termout off
column uservar new_value Table_Owner noprint
select user uservar from dual;
set termout on
column TABLE_NAME heading "Tables owned by &Table_Owner" format a30
select table_name from dba_tables where owner=upper('&Table_Owner') order by 1
/
undefine table_name
undefine owner
prompt
accept owner prompt 'Please enter Name of Table Owner (Null = &Table_Owner): '
accept table_name prompt 'Please enter Table Name to show Statistics for: '
column TABLE_NAME heading "Table|Name" format a15
column PARTITION_NAME heading "Partition|Name" format a15
column SUBPARTITION_NAME heading "SubPartition|Name" format a15
column NUM_ROWS heading "Number|of Rows" format 9,999,999,990
column BLOCKS heading "Blocks" format 999,990
column EMPTY_BLOCKS heading "Empty|Blocks" format 999,999,990

column AVG_SPACE heading "Average|Space" format 9,990
column CHAIN_CNT heading "Chain|Count" format 999,990
column AVG_ROW_LEN heading "Average|Row Len" format 990
column COLUMN_NAME heading "Column|Name" format a25
column NULLABLE heading Null|able format a4
column NUM_DISTINCT heading "Distinct|Values" format 999,999,990
column NUM_NULLS heading "Number|Nulls" format 9,999,990
column NUM_BUCKETS heading "Number|Buckets" format 990
column DENSITY heading "Density" format 990
column INDEX_NAME heading "Index|Name" format a15
column UNIQUENESS heading "Unique" format a9
column BLEV heading "B|Tree|Level" format 90
column LEAF_BLOCKS heading "Leaf|Blks" format 990
column DISTINCT_KEYS heading "Distinct|Keys" format 9,999,999,990
column AVG_LEAF_BLOCKS_PER_KEY heading "Average|Leaf Blocks|Per Key" format 99,990
column AVG_DATA_BLOCKS_PER_KEY heading "Average|Data Blocks|Per Key" format 99,990
column CLUSTERING_FACTOR heading "Cluster|Factor" format 999,999,990
column COLUMN_POSITION heading "Col|Pos" format 990
column col heading "Column|Details" format a24
column COLUMN_LENGTH heading "Col|Len" format 9,990
column GLOBAL_STATS heading "Global|Stats" format a6
column USER_STATS heading "User|Stats" format a6
column SAMPLE_SIZE heading "Sample|Size" format 9,999,999,990
column to_char(t.last_analyzed,'MM-DD-YYYY') heading "Date|MM-DD-YYYY" format a10

prompt
prompt ***********
prompt Table Level
prompt ***********
prompt
select
TABLE_NAME,
NUM_ROWS,
BLOCKS,
EMPTY_BLOCKS,
AVG_SPACE,
CHAIN_CNT,
AVG_ROW_LEN,
GLOBAL_STATS,
USER_STATS,
SAMPLE_SIZE,
to_char(t.last_analyzed,'MM-DD-YYYY')
from dba_tables t
where
owner = upper(nvl('&&Owner',user))
and table_name = upper('&&Table_name')
/
select
COLUMN_NAME,
decode(t.DATA_TYPE,
'NUMBER',t.DATA_TYPE||'('||
decode(t.DATA_PRECISION,
null,t.DATA_LENGTH||')',
t.DATA_PRECISION||','||t.DATA_SCALE||')'),
'DATE',t.DATA_TYPE,
'LONG',t.DATA_TYPE,
'LONG RAW',t.DATA_TYPE,
'ROWID',t.DATA_TYPE,
'MLSLABEL',t.DATA_TYPE,
t.DATA_TYPE||'('||t.DATA_LENGTH||')') ||' '||
decode(t.nullable,
'N','NOT NULL',
'n','NOT NULL',
NULL) col,
NUM_DISTINCT,
DENSITY,
NUM_BUCKETS,
NUM_NULLS,
GLOBAL_STATS,
USER_STATS,
SAMPLE_SIZE,
to_char(t.last_analyzed,'MM-DD-YYYY')
from dba_tab_columns t
where
table_name = upper('&Table_name')
and owner = upper(nvl('&Owner',user))
/

select
INDEX_NAME,
UNIQUENESS,
BLEVEL BLev,
LEAF_BLOCKS,
DISTINCT_KEYS,
NUM_ROWS,
AVG_LEAF_BLOCKS_PER_KEY,
AVG_DATA_BLOCKS_PER_KEY,
CLUSTERING_FACTOR,
GLOBAL_STATS,
USER_STATS,
SAMPLE_SIZE,
to_char(t.last_analyzed,'MM-DD-YYYY')
from
dba_indexes t
where
table_name = upper('&Table_name')
and table_owner = upper(nvl('&Owner',user))
/
break on index_name
select
i.INDEX_NAME,
i.COLUMN_NAME,
i.COLUMN_POSITION,
decode(t.DATA_TYPE,
'NUMBER',t.DATA_TYPE||'('||
decode(t.DATA_PRECISION,
null,t.DATA_LENGTH||')',
t.DATA_PRECISION||','||t.DATA_SCALE||')'),
'DATE',t.DATA_TYPE,
'LONG',t.DATA_TYPE,
'LONG RAW',t.DATA_TYPE,
'ROWID',t.DATA_TYPE,
'MLSLABEL',t.DATA_TYPE,
t.DATA_TYPE||'('||t.DATA_LENGTH||')') ||' '||
decode(t.nullable,
'N','NOT NULL',
'n','NOT NULL',
NULL) col
from
dba_ind_columns i,
dba_tab_columns t
where
i.table_name = upper('&Table_name')
and owner = upper(nvl('&Owner',user))
and i.table_name = t.table_name
and i.column_name = t.column_name
order by index_name,column_position
/

prompt
prompt ***************
prompt Partition Level
prompt ***************

select
PARTITION_NAME,
NUM_ROWS,
BLOCKS,
EMPTY_BLOCKS,
AVG_SPACE,
CHAIN_CNT,
AVG_ROW_LEN,
GLOBAL_STATS,
USER_STATS,
SAMPLE_SIZE,
to_char(t.last_analyzed,'MM-DD-YYYY')
from
dba_tab_partitions t
where
table_owner = upper(nvl('&&Owner',user))
and table_name = upper('&&Table_name')
order by partition_position
/


break on partition_name
select
PARTITION_NAME,
COLUMN_NAME,
NUM_DISTINCT,
DENSITY,
NUM_BUCKETS,
NUM_NULLS,
GLOBAL_STATS,
USER_STATS,
SAMPLE_SIZE,
to_char(t.last_analyzed,'MM-DD-YYYY')
from
dba_PART_COL_STATISTICS t
where
table_name = upper('&Table_name')
and owner = upper(nvl('&Owner',user))
/

break on partition_name
select
t.INDEX_NAME,
t.PARTITION_NAME,
t.BLEVEL BLev,
t.LEAF_BLOCKS,
t.DISTINCT_KEYS,
t.NUM_ROWS,
t.AVG_LEAF_BLOCKS_PER_KEY,
t.AVG_DATA_BLOCKS_PER_KEY,
t.CLUSTERING_FACTOR,
t.GLOBAL_STATS,
t.USER_STATS,
t.SAMPLE_SIZE,
to_char(t.last_analyzed,'MM-DD-YYYY')
from
dba_ind_partitions t,
dba_indexes i
where
i.table_name = upper('&Table_name')
and i.table_owner = upper(nvl('&Owner',user))
and i.owner = t.index_owner
and i.index_name=t.index_name
/


prompt
prompt ***************
prompt SubPartition Level
prompt ***************

select
PARTITION_NAME,
SUBPARTITION_NAME,
NUM_ROWS,
BLOCKS,
EMPTY_BLOCKS,
AVG_SPACE,
CHAIN_CNT,
AVG_ROW_LEN,
GLOBAL_STATS,
USER_STATS,
SAMPLE_SIZE,
to_char(t.last_analyzed,'MM-DD-YYYY')
from
dba_tab_subpartitions t
where
table_owner = upper(nvl('&&Owner',user))
and table_name = upper('&&Table_name')
order by SUBPARTITION_POSITION
/
break on partition_name
select
p.PARTITION_NAME,
t.SUBPARTITION_NAME,
t.COLUMN_NAME,
t.NUM_DISTINCT,
t.DENSITY,
t.NUM_BUCKETS,
t.NUM_NULLS,
t.GLOBAL_STATS,
t.USER_STATS,
t.SAMPLE_SIZE,
to_char(t.last_analyzed,'MM-DD-YYYY')
from
dba_SUBPART_COL_STATISTICS t,
dba_tab_subpartitions p
where
t.table_name = upper('&Table_name')
and t.owner = upper(nvl('&Owner',user))
and t.subpartition_name = p.subpartition_name
and t.owner = p.table_owner
and t.table_name=p.table_name
/

break on partition_name
select
t.INDEX_NAME,
t.PARTITION_NAME,
t.SUBPARTITION_NAME,
t.BLEVEL BLev,
t.LEAF_BLOCKS,
t.DISTINCT_KEYS,
t.NUM_ROWS,
t.AVG_LEAF_BLOCKS_PER_KEY,
t.AVG_DATA_BLOCKS_PER_KEY,
t.CLUSTERING_FACTOR,
t.GLOBAL_STATS,
t.USER_STATS,
t.SAMPLE_SIZE,
to_char(t.last_analyzed,'MM-DD-YYYY')
from
dba_ind_subpartitions t,
dba_indexes i
where
i.table_name = upper('&Table_name')
and i.table_owner = upper(nvl('&Owner',user))
and i.owner = t.index_owner
and i.index_name=t.index_name
/

你可能感兴趣的:(sql,user,table,null,delete,statistics)