直接查dba_segments会严重低估含lob表的真实大小,因其仅返回表段空间,而lob数据存于独立lob段(如sys_lob000012345c00006$$),需联合dba_lobs和dba_indexes(index_type='lob')分别统计表段、lob段及lob索引段,并用nvl(sum(),0)避免null导致结果丢失。

只查 DBA_SEGMENTS 会严重低估带 LOB 的表大小
直接用 SEGMENT_NAME = 'YOUR_TABLE' 在 DBA_SEGMENTS 里查,得到的只是表段(table segment)——即行头 + 非 LOB 列数据。CLOB/BLOB 内容实际存在独立的 LOB 段里,名字像 SYS_LOB000012345C00006$$,和表名完全无关。漏掉这部分,对大文本/图片表可能少算 80% 以上空间。
-
DBA_LOBS的SEGMENT_NAME字段才是 LOB 数据所在段名,它和DBA_SEGMENTS.SEGMENT_NAME关联,不是和表名关联 - 每个 LOB 列默认带一个隐式索引,类型为
'LOB',也单独占空间,必须通过DBA_INDEXES.INDEX_TYPE = 'LOB'过滤,否则会混入普通索引 - 如果表在多个表空间(比如表段在 USERS、LOB 段在 LOB_TBS),各部分要分别查,不能只看
DBA_TABLES.TABLESPACE_NAME
DBA_LOBS 和 DBA_INDEXES 关联时容易写错条件
常见错误是把 DBA_LOBS.TABLE_NAME 当成关联主键,或者忽略 owner 匹配。正确关联逻辑是:DBA_LOBS.SEGMENT_NAME = DBA_SEGMENTS.SEGMENT_NAME,且三者 OWNER 必须一致。
- 必须加
AND L.OWNER = S.OWNER,否则跨 schema 查询可能命中其他用户的同名段 -
DBA_INDEXES关联时,用I.INDEX_NAME = S.SEGMENT_NAME,不是I.TABLE_NAME = S.SEGMENT_NAME - 漏掉
AND I.INDEX_TYPE = 'LOB'会导致普通索引被计入,结果虚高
计算总大小必须分三路求和再相加
不能靠单条 SUM() 加子查询拼接就完事——某部分为空时,整个表达式会返回 NULL。必须每路都用 NVL(SUM(), 0) 保底。
- 表段:查
DBA_SEGMENTS中SEGMENT_NAME = UPPER('&TABNAME')且OWNER = UPPER('&SCHEMA') - LOB 段:联查
DBA_SEGMENTS S和DBA_LOBS L,条件是S.SEGMENT_NAME = L.SEGMENT_NAME且L.TABLE_NAME = UPPER('&TABNAME') - LOB 索引段:联查
DBA_SEGMENTS S和DBA_INDEXES I,条件是S.SEGMENT_NAME = I.INDEX_NAME且I.INDEX_TYPE = 'LOB'
分区表或压缩表的额外影响
如果表是分区的,DBA_SEGMENTS 里对应多个段(每个分区一个),上面三路查询仍适用——DBA_LOBS 和 DBA_INDEXES 本身已按分区粒度建模,无需额外处理。但注意:
- 压缩表(
COMPRESS FOR OLTP或ADVANCED)不影响段大小统计逻辑,BYTES字段反映的是分配空间,不是压缩后体积 - 如果 LOB 列启用了
DISABLE STORAGE IN ROW,所有 LOB 数据强制存外,DBA_LOBS查出的大小更接近真实值;反之启用ENABLE STORAGE IN ROW时,小 LOB(≤ 3964 字节)可能内联存储,不计入 LOB 段 - 执行前确认权限:需要
SELECT_CATALOG_ROLE或直接SELECT权限在DBA_*视图上
STORAGE IN ROW ——这些都会让“理论大小”和“实际占用”产生偏差。查之前先跑一遍 SELECT TABLE_NAME, COLUMN_NAME, SEGMENT_NAME FROM DBA_LOBS WHERE OWNER = 'XXX',心里有数再动手。











