一、传统统计的「死亡陷阱」

1.1 盲目COUNT(*):像在玩「俄罗斯轮盘赌」
SELECT COUNT(*) FROM BIG_TABLE; -- 10亿条数据 → 数据库直接卡成PPT  

DBA的血泪史
“我数个数据,你直接让我等1小时?
索引?分区?全表扫描直接把我打成『数据苦力』!”


1.2 统计信息过期:像在看「过期天气预报」
SELECT NUM_ROWS FROM USER_TABLES WHERE TABLE_NAME = 'MY_TABLE'; -- 显示0?!  

灵魂拷问
“为什么NUM_ROWS是0?
因为统计信息上次更新还是上个世纪!”


二、5种魔法咒语:让数据量「显形」

2.1 咒语1:数据字典的「透视眼」
-- 查看所有表的统计信息(秒速查询!)  
SELECT  
    TABLE_NAME,  
    NUM_ROWS AS "预估行数",  
    ROUND((BLOCKS * 8192)/1024/1024, 2) || 'MB' AS "预估大小"  
FROM  
    USER_TABLES  
ORDER BY  
    NUM_ROWS DESC;  

-- 🧙♂️ 魔法解析:  
-- 1. NUM_ROWS:统计信息中的行数(可能不准确,需定期更新)  
-- 2. BLOCKS:数据块数 × 8KB/块 = 总大小(单位MB)  

程序员的冷笑话
这个咒语像在说:
“我知道你有数据,但我不用真的数!”


2.2 咒语2:分区表的「分身术」
-- 统计分区表的每个分区数据量  
SELECT  
    PARTITION_NAME,  
    NUM_ROWS AS "分区行数",  
    ROUND((BYTES/1024/1024), 2) || 'MB' AS "分区大小"  
FROM  
    USER_TAB_PARTITIONS  
WHERE  
    TABLE_NAME = 'LOG_PARTITIONED';  

-- 🧙♂️ 魔法解析:  
-- 1. USER_TAB_PARTITIONS:分区表的统计信息视图  
-- 2. BYTES:分区占用的字节数(精确到分区级别)  

灵魂拷问
为什么分区统计更准?
因为它像「分块扫描」,效率高到飞起!


2.3 咒语3:COUNT(*)的「加速版」
-- 用索引快速统计(需有索引!)  
SELECT  
    COUNT(*)  
FROM  
    MY_TABLE  
WHERE  
    1 = 1  
    AND 1 = 0; -- 这行代码是陷阱!  

-- 🧙♂️ 魔法解析:  
-- 1. 添加永远为假的条件(1=0)  
-- 2. 让Oracle执行计划走索引而不扫描全表  
-- 3. 通过执行计划中的「RETURN ROWS」查看行数  

-- 🚨 注意:  
-- 需要查看执行计划中的「Rows」列,实际不会返回数据!  

程序员的浪漫
这段代码像在说:
“我假装要数据,其实只想知道大概有多少!”


2.4 咒语4:闪回查询的「时光机」
-- 查看过去的数据量(需开启闪回)  
SELECT  
    COUNT(*)  
FROM  
    MY_TABLE  
AS OF TIMESTAMP (SYSTIMESTAMP - INTERVAL '1' HOUR);  

-- 🧙♂️ 魔法解析:  
-- 1. AS OF TIMESTAMP:时间点查询  
-- 2. 需要表有闪回数据档案(Flashback Data Archive)  

DBA的哲学
这段代码像在说:
“我要回到1小时前,看看数据是什么样子!”


2.5 咒语5:动态性能视图的「终极解密」
-- 查看实时数据量(需DBA权限)  
SELECT  
    SEGMENT_NAME AS "表名",  
    PARTITION_NAME AS "分区",  
    ROUND((BYTES/1024/1024), 2) || 'MB' AS "当前大小",  
    LAST_ANALYZED AS "统计信息更新时间"  
FROM  
    DBA_SEGMENTS  
WHERE  
    SEGMENT_TYPE = 'TABLE'  
    AND OWNER = 'MY_SCHEMA';  

-- 🧙♂️ 魔法解析:  
-- 1. DBA_SEGMENTS:存储段信息的视图  
-- 2. BYTES:物理存储大小(含空闲块)  

灵魂拷问
为什么DBA_SEGMENTS最准?
因为它直接看「数据库的体检报告」!


三、终极优化:让统计「跑得比风还快」

3.1 统计信息更新:给数据字典「打疫苗」
-- 更新表统计信息(需DBA权限)  
BEGIN  
    DBMS_STATS.GATHER_TABLE_STATS(  
        ownname => 'MY_SCHEMA',  
        tabname => 'MY_TABLE',  
        estimate_percent => 10, -- 采样10%  
        method_opt => 'FOR ALL COLUMNS SIZE AUTO'  
    );  
END;  
/  

-- 🧙♂️ 魔法解析:  
-- 1. estimate_percent:采样比例(100%最准但最慢)  
-- 2. method_opt:自动优化直方图  

DBA的智慧
这段代码像在说:
“我要重新给数据做体检,统计信息要更新!”


3.2 索引加速:给COUNT(*)装个「火箭」
-- 创建索引加速统计  
CREATE INDEX idx_count ON MY_TABLE (1); -- 伪列索引  

-- 使用索引统计:  
SELECT  
    COUNT(*)  
FROM  
    MY_TABLE  
WHERE  
    1 = 1  
    AND 1 = 0; -- 利用伪列索引快速统计  

-- 🧙♂️ 魔法解析:  
-- 1. 索引中的伪列(1)始终存在  
-- 2. Oracle通过索引统计信息快速返回行数  

程序员的哲学
这段代码像在说:
“我要用索引的『记忆』代替全表扫描!”


四、实战案例:拯救「百万级表」的统计危机

4.1 场景:运维发现表数据量异常增长

凌晨3点,监控大屏炸出红色警报
“日志表突然暴涨到100亿条!磁盘空间还剩1%!”

4.2 解决方案:多咒语组合拳
-- 步骤1:快速定位增长分区  
SELECT  
    PARTITION_NAME,  
    NUM_ROWS,  
    LAST_ANALYZED  
FROM  
    USER_TAB_PARTITIONS  
WHERE  
    TABLE_NAME = 'LOG_TABLE'  
ORDER BY  
    NUM_ROWS DESC;  

-- 步骤2:强制更新统计信息  
BEGIN  
    DBMS_STATS.GATHER_TABLE_STATS(  
        ownname => 'LOG_SCHEMA',  
        tabname => 'LOG_TABLE',  
        granularity => 'PARTITION'  
    );  
END;  
/  

-- 步骤3:删除过期分区  
ALTER TABLE LOG_TABLE DROP PARTITION p_202301; -- 删除2023年1月分区  

事后复盘
这波操作让数据量从100亿降到1亿,
磁盘空间瞬间恢复到80%,
老板直接夸我:「你的SQL比我的咖啡还提神!」


五、防坑指南:避免统计的「四大天坑」

5.1 坑位1:直接用COUNT(*),像「用锤子量体温」

反例:在百亿级表上运行COUNT(*)
正确姿势:用数据字典或索引加速

5.2 坑位2:统计信息过期,像「参考过期地图」

反例:依赖1年前的NUM_ROWS
正确姿势:定期执行DBMS_STATS


Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐