为何Oracle中NUM_ROWS>0的非空表并非全部分配有对应数据块?
问题原因说明
查询出现大量不匹配的核心原因是表关联逻辑完全错误,其次即使修正关联逻辑,也存在部分符合NUM_ROWS>0的表不会在dba_segments中存在对应TABLE类型段的场景,具体说明如下:
1. 核心错误:关联键使用错误
当前SQL写的JOIN条件是seg.BLOCKS = at.BLOCKS,这完全不是all_tables和dba_segments的合法关联逻辑:
- 两个视图的正确关联维度是对象所有者、对象名、对象类型,对应关联条件应该是
seg.owner = at.owner AND seg.segment_name = at.table_name AND seg.segment_type = 'TABLE' all_tables.BLOCKS字段存储的是统计信息收集时刻记录的表已使用块数,属于统计估算值;dba_segments.BLOCKS存储的是段当前实际分配的总块数,两个值本身就没有强一致要求:统计信息过期、表做过增删改操作未重新收集统计、段做过扩展/收缩/截断操作,都会导致两个字段值不相等,用块数相等做关联条件,必然出现大量漏匹配、错匹配,这也是结果差值达到-564的主要原因。
2. 即使关联逻辑正确,仍会存在匹配不上的场景
修正关联条件后,以下几类NUM_ROWS>0的表依然不会在dba_segments中匹配到对应TABLE类型的段:
- 分区表:分区表的存储段是分配给每个分区/子分区的,段类型为
TABLE PARTITION/TABLE SUBPARTITION,父表本身不会创建独立的TABLE类型段 - 簇表:簇表的存储段归属于整个簇对象,段类型为
CLUSTER,单张簇表不会单独分配TABLE类型段 - 外部表:外部表的数据存储在数据库外部的操作系统文件中,数据库内不会为其分配存储段,
dba_segments中无对应记录 - 全局/私有临时表:临时表的段是会话/事务级动态创建的,若当前查询会话未对临时表执行过写入操作,
dba_segments中不会存在对应段;而all_tables.NUM_ROWS是历史统计信息值,只要之前收集统计信息时表中有数据,就会显示大于0 - 权限不足:
dba_segments查询需要对应字典权限,若当前用户没有查看其他用户段对象的权限,即使能在all_tables中看到其他用户的表,也无法在dba_segments中查到对应段记录
修正后的参考统计SQL
SELECT (SELECT COUNT(DISTINCT at.owner, at.table_name) FROM all_tables at WHERE at.NUM_ROWS > 0 AND EXISTS ( SELECT 1 FROM dba_segments seg WHERE seg.owner = at.owner AND seg.segment_name = at.table_name AND seg.segment_type = 'TABLE' )) - (SELECT COUNT(DISTINCT at.owner, at.table_name) FROM all_tables at WHERE at.NUM_ROWS > 0) FROM DUAL;
注意:上述SQL仅统计普通堆表的匹配情况,如果需要覆盖分区表、簇表等特殊表类型,需要额外关联
dba_tab_partitions、dba_clusters等视图做匹配。
内容的提问来源于stack exchange,提问作者Pierre-olivier Gendraud
相关产品推荐
相关产品推荐

