不能直接 GROUP BY OWNER,因为OWNER是schema名而非存储单位,同一schema的对象可分散在多个表空间,且DBA_SEGMENTS包含各类段类型,混计会导致结果失真。
dba_segments 是 oracle 中最直接反映物理段(table、index、lobsegment 等)空间占用的视图,但按 owner 分组统计“各方案占用的表空间”时,必须注意:owner 不等于表空间(tablespace_name),且不同段类型可能跨表空间存储。直接 sum(bytes) 分组会把索引、lob、临时段等全混在一起,结果严重失真。
为什么不能直接 GROUP BY OWNER?
常见错误是写成这样:
SELECT owner, SUM(bytes)/1024/1024 AS mb FROM dba_segments GROUP BY owner ORDER BY mb DESC;
这看似简洁,但问题极多:
-
OWNER是 schema 名,不是存储单位;一个 schema 的对象可能分散在多个表空间(比如表在USERS,索引在INDEXES,LOB 在LOB_TBS) -
dba_segments包含所有段类型:'TABLE'、'INDEX'、'LOBSEGMENT'、'LOBINDEX'、'TABLE PARTITION'等——不加过滤会导致重复计数或误判业务表真实体量 - 如果用户有大量无效索引或历史分区,
SUM会高估其“业务数据”实际占用
正确做法:先限定 segment_type,再关联逻辑归属
要回答“某 OWNER 下业务表真正占了多少空间”,核心是聚焦三类段,并明确归属关系:
-
segment_type = 'TABLE'→ 表数据段(不含 LOB 内容) -
segment_type = 'LOBSEGMENT'→ 对应表中 CLOB/BLOB 字段的实际存储 -
segment_type = 'INDEX'→ 该表的索引(需通过dba_indexes.table_owner关联)
推荐用以下 SQL 按 OWNER 统计“表+LOB+主键/唯一索引”的总空间(单位 GB,四舍五入):
SELECT
s.owner,
ROUND(SUM(s.bytes) / 1024 / 1024 / 1024, 2) AS total_gb
FROM dba_segments s
WHERE s.segment_type IN ('TABLE', 'LOBSEGMENT')
OR (s.segment_type = 'INDEX'
AND s.segment_name IN (
SELECT i.index_name
FROM dba_indexes i
WHERE i.table_owner = s.owner AND i.uniqueness IN ('UNIQUE', 'PRIMARY')
))
GROUP BY s.owner
ORDER BY total_gb DESC;说明:
- 排除普通非唯一索引(如查询索引),避免把运维型索引计入“方案体量”
-
LOBSEGMENT自动绑定到所属表的owner,无需额外 JOIN - 若需包含所有索引,把
i.uniqueness IN (...)改为1=1,但务必意识到这会让统计值显著膨胀
权限与性能关键点
执行上述查询前确认:
- 你拥有
SELECT_CATALOG_ROLE或显式SELECTonDBA_SEGMENTS、DBA_INDEXES—— 否则换用ALL_SEGMENTS+ALL_INDEXES,但只能看到有访问权限的对象 - 大库中
dba_segments可能超百万行,建议加WHERE owner IN (...)限定范围,避免全扫 - 不要在生产高峰跑无 WHERE 的聚合;可先用
COUNT(*)预估数据量:SELECT COUNT(*) FROM dba_segments WHERE owner = 'C##BBP402CYTJDEV41';
最后提醒:dba_segments.bytes 是已分配空间,不是实际数据量。空块、HWM 以上未使用空间、延迟段(deferred_segment_creation=TRUE)都包含在内——想看真实行数据体积,得查 dba_tables.num_rows × avg_row_len 并估算,那是另一层精度了。


















