查用户表大小必须用user_segments而非user_tables,因后者仅存元数据;user_segments中segment_name需严格匹配(默认大写),且同一表可能有多个segment(如LOB)。
查用户表大小必须用 user_segments,不是 user_tables
user_tables 只存元数据(比如列名、状态),不记录空间占用。真正反映物理大小的是 user_segments——它对应磁盘上实际分配的段(segment)。误用 user_tables 会导致查不到任何字节数,甚至报“no rows selected”。
关键点:
-
segment_name必须大写,且与表名完全一致(Oracle 默认转大写,但若建表时加了双引号定义小写名,则需按原样匹配) - 同一个表可能有多个 segment(如带 LOB 列时,
LOBSEGMENT和LOBINDEX会单独计) - 查询前确保你有访问
user_segments的权限;普通用户默认可查,但若被显式收回SELECT_CATALOG_ROLE,则会报ORA-00942: table or view does not exist
示例(查当前用户下 EMPLOYEES 表总大小):
SELECT SUM(bytes) / 1024 / 1024 AS "size_mb" FROM user_segments WHERE segment_name = 'EMPLOYEES';
dba_segments 和 user_segments 权限差异直接影响结果范围
用 dba_segments 能看到所有用户的对象大小,但前提是已授予 SELECT ANY DICTIONARY 或 DBA 角色。没权限时直接执行会报 ORA-00942,而不是返回空结果——这点容易误判为“没数据”。
常见错误场景:
- 开发连测试库用普通账号,却复制 DBA 脚本里的
dba_segments查询,结果报错 - 误以为
all_segments是“所有用户+自己”,其实它只显示当前用户有权限访问的对象(比如被授予SELECT的其他用户表),大小值也仅反映你能读到的部分 - 在 RAC 环境中,
dba_segments默认只查当前实例,跨实例需加@dblink或切到对应实例连接
安全做法:先确认角色
SELECT granted_role FROM session_roles WHERE granted_role IN ('DBA', 'SELECT_ANY_DICTIONARY');
精确查某张表真实数据量,得用 dbms_space.object_space_usage
user_segments.bytes 返回的是分配空间(包括未使用的高水位线以上空间),而 dbms_space.object_space_usage 能给出已使用字节数、行数估算、链式行比例等更贴近实际的数据。
注意限制:
- 必须用 PL/SQL 块执行,不能直接 SELECT
- 需要
EXECUTE权限 ondbms_space(通常随CONNECT或RESOURCE角色附带,但某些最小化安装可能未赋) -
segment_owner参数填当前用户名即可,填错(如小写或空格)会报ORA-32034: unsupported use of WITH clause类似误导性错误
简化版调用(查当前用户下的 ORDERS):
BEGIN
dbms_space.object_space_usage(
segment_owner => USER,
segment_name => 'ORDERS',
segment_type => 'TABLE',
used_bytes => :used,
alloc_bytes => :alloc
);
END;
其中 :used 和 :alloc 是绑定变量,需在支持绑定的客户端(如 SQL*Plus、SQL Developer)中定义并打印。
查视图/索引/LOB 对象大小容易漏掉类型过滤
一张表的空间可能分散在多个 segment 类型里:TABLE、INDEX、LOBSEGMENT、LOBINDEX、MATERIALIZED VIEW。如果只查 segment_type = 'TABLE',会严重低估总占用。
实操建议:
- 先看全貌:
SELECT segment_type, COUNT(*) FROM user_segments GROUP BY segment_type; - 对含 CLOB/BLOB 的表,务必补查:
WHERE segment_name LIKE 'SYS_LOB%' OR segment_name LIKE 'SYS_IL%'(系统生成的 LOB 段名有固定前缀) - 物化视图日志(
MVIEW LOG)也会产生TABLE类型 segment,但名字带RUPD$_前缀,容易被忽略
最简汇总语句(含常见类型):
SELECT segment_type, SUM(bytes)/1024/1024 AS mb FROM user_segments WHERE segment_name IN ( SELECT table_name FROM user_tables UNION SELECT index_name FROM user_indexes UNION SELECT segment_name FROM user_lobs ) GROUP BY segment_type;
真实环境里,LOB 和索引常占表总大小的 30%–200%,跳过它们等于只看了冰山一角。











