直接查 dba_segments 的分区段会失真,因为每个分区在该视图中是独立记录,按 segment_name 过滤会割裂逻辑整体;且表分区、索引分区、lob 分区混杂,须按 owner+segment_name+partition_name+segment_type 精确分组聚合才能反映真实占用。

为什么直接查 DBA_SEGMENTS 的分区段会失真
因为一张分区表的每个分区在 DBA_SEGMENTS 里都是独立记录,比如 SALES_2025_Q1、SALES_2025_Q2 各自占一行,但它们逻辑上属于同一张表。如果只按 segment_name 过滤或排序,就等于把一个整体拆成碎片去比大小——结果前 10 名全是小分区,真正吃空间的 SALES 主体反而看不见。
更麻烦的是,分区段还常和 LOB 段、索引段混在一起:一个带 CLOB 的分区表,可能有 12 个分区段 + 12 个 LOBSEGMENT + 若干 INDEX 分区段,不归类聚合根本没法判断“哪个分区实际最胖”。
必须按 OWNER + SEGMENT_NAME + PARTITION_NAME 聚合
想看清单个分区的真实物理占用,聚合维度不能漏掉 PARTITION_NAME,否则会把不同分区的数据加总到一起,失去分区级视角;但也不能只看它——得带上 OWNER 和 SEGMENT_NAME 才能准确定位到具体对象。
-
SEGMENT_TYPE = 'TABLE PARTITION'是主数据分区段 -
SEGMENT_TYPE = 'INDEX PARTITION'是对应索引分区,需单独查,别混进表分区统计里 -
SEGMENT_TYPE = 'LOB PARTITION'必须显式包含,尤其当表含 CLOB/BLOB 字段时,这部分可能比主分区大 5–10 倍 - 聚合后用
SUM(bytes)算总字节数,再除以 1024/1024/1024 转 GB,保留两位小数即可
常见错误:混淆分区段和非分区段的统计口径
执行类似 SELECT * FROM DBA_SEGMENTS WHERE SEGMENT_NAME LIKE 'SALES%' 这种模糊查询,极大概率捞出一堆 SALES_IDX(索引)、SALES_LOB(LOB 段)、SALES_2025_Q1(分区)混在一起的结果,既无法对齐业务语义,也无法做容量趋势分析。
正确做法是先明确目标:
- 查“某张分区表的每个分区有多大” → 过滤
SEGMENT_TYPE IN ('TABLE PARTITION', 'LOB PARTITION', 'INDEX PARTITION'),再按OWNER, SEGMENT_NAME, PARTITION_NAME, SEGMENT_TYPE分组 - 查“某分区表所有分区合计多大” → 同上过滤,但只按
OWNER, SEGMENT_NAME分组,忽略PARTITION_NAME - 查“整个用户下哪些分区最占空间” → 不加
SEGMENT_NAME过滤,但必须保留PARTITION_NAME IS NOT NULL条件,排除非分区对象干扰
FETCH FIRST 10 ROWS ONLY 比 ROWNUM 更可靠
ROWNUM 在 Oracle 中是在结果集生成过程中逐行编号的,它发生在 ORDER BY 之前。如果你写 WHERE ROWNUM 却没套子查询,Oracle 会先取前 10 行、再排序——那 10 行根本不是最大的。
用 FETCH FIRST 10 ROWS ONLY 就没这个问题,它是标准 SQL:2008 语法,语义清晰,且自动保证先排序后截断:
SELECT owner, segment_name, partition_name, segment_type,
ROUND(SUM(bytes)/1024/1024/1024, 2) AS gb
FROM dba_segments
WHERE segment_type IN ('TABLE PARTITION', 'LOB PARTITION', 'INDEX PARTITION')
AND partition_name IS NOT NULL
GROUP BY owner, segment_name, partition_name, segment_type
ORDER BY SUM(bytes) DESC
FETCH FIRST 10 ROWS ONLY;
注意:这个语句在 Oracle 12c 及以上才支持;低于 12c 必须用子查询套一层 ROWNUM,但要小心别漏掉外层 ORDER BY。
分区段的物理大小不是静态值——它随 INSERT/DELETE/SHRINK 操作实时变化,而且 bytes 统计的是已分配空间,不是真实数据量。如果发现某个分区显示 50GB 但 COUNT(*) 只有几万行,优先检查是否做过大量 DELETE 未收缩,或者 LOB 数据滞留未清理。











