本文基于 Oracle 11g版本进行演示sql
或许咱们平时有这么一些疑问,当你想统计一个查询的总记录数时,第一时间就想到count(*)
来实现,但忽然又担忧该表数据量大时,担忧count(*)
性能会不好,因而乎,咱们可能会使用count(列)
进行统计,那到底这两个有什么区别呢?缓存
-- 若是存在,则删除该表
DROP TABLE TEST_TABLE;
-- 基于DBA_OBJECTS创建一张测试表,这张表是没有任何主键、外键、索引的
CREATE TABLE TEST_TABLE AS (SELECT * FROM DBA_OBJECTS);
复制代码
咱们获得一个TEST_TABLE
表,用于咱们本身测试,该表派生于DBA_OBJECTS
表,注意只有sys
用户才能使用这张表,请使用sys
进行登陆。性能优化
SELECT COUNT(*) FROM TEST_TABLE;
bash
第一次:oracle
CPU开销:281,递归调用:28,一致性读:1097
post
第二次:性能
CPU开销:281,递归调用:0,一致性读:1031
这里你会发现,开销同样,可是递归调用竟然变成0,一致性读也变少了不少,为何呢?由于oracle执行完请求以后会将数据缓存到cache中,对应oracle的share pool区域测试
SELECT COUNT(OBJECT_ID) FROM TEST_TABLE;
优化
第一次:spa
CPU开销:281,递归调用:27,一致性读:1097
第二次:
CPU开销:281,递归调用:0,一致性读:1031
你会发现,其实COUNT(*)
和COUNT(OBJECT_ID)
的效率是同样快的。你可能会不服气,你干吗选OBJECT_ID
做为演示,你干脆换个别的列看看,好的,下面演示别的列:
SELECT COUNT(STATUS) FROM TEST_TABLE;
第一次:
CPU开销:281,递归调用:4,一致性读:1095
第二次:
CPU开销:281,递归调用:0,一致性读:1031
结果仍是1031个一致性读
,同样快啊~我仍是不信,你选择有不少空值的列SUBOBJECT_NAME
这个列进行演示吧!好,看下面脚本: SELECT COUNT(SUBOBJECT_NAME) FROM TEST_TABLE;
第一次:
CPU开销:281,递归调用:4,一致性读:1095
第二次:
CPU开销:281,递归调用:0,一致性读:1031
是吧!仍是同样,一致性读都是1031
,效率是没有差异的。可是,你发现没有,最后两张图我格外圈了个小圈圈,总条数怎么变成357了呢?不该该是72056行吗? 这是因为COUNT(列)
在统计时,对于该列的值如果为null,则不参加计算,这是说明,其实SELECT COUNT(*) FROM TEST_TABLE
和SELECT COUNT(列) FROM TEST_TABLE
是不等价的,若是你拿SELECT COUNT(列) FROM XXX
去完成一个统计表记录数的需求,那GG。
上面实验证实了,在没有索引的前提下,COUNT(*)
和COUNT(列)
是没有差异的。假设咱们给OBJECT_ID
这个列加上一个索引会怎样,执行下面脚本: CREATE INDEX OBJECT_ID_INDEX ON TEST_TABLE(OBJECT_ID);
而后咱们再执行下面脚本:
SELECT COUNT(*) FROM TEST_TABLE;
第二遍:
CPU开销:281,递归调用:0,一致性读:1031
从上图能够看出,即便咱们创建了索引,COUNT(*)
也不会走索引,反而从执行计划能够看出走了全表扫描TABLE ACCESS FULL
,关于执行计划的相关内容,可阅读小编另一篇文章【Oracle性能优化一】执行计划与索引类型分析
那么此时COUNT(OBJECT_ID)
效果如何呢?执行下面脚本: SELECT COUNT(OBJECT_ID) FROM TEST_TABLE;
CPU开销:45,递归调用:0,一致性读:168
第二遍发现CPU开销和一致性读都变少了不少,并且还走了索引扫描INDEX FAST FULL SCAN
,性能确实了得。固然了,这是针对索引列的COUNT(索引列)
,非索引列效果如何呢?执行下面脚本:
SELECT COUNT(SUBOBJECT_NAME) FROM TEST_TABLE;
CPU开销:281,递归调用:0,一致性读:1031
发现COUNT(非索引列)
没有太大变化。有个小问题,咱们那个OBJECT_ID
是容许为空的,假设非空会怎样呢?执行下面脚本:
ALTER TABLE TEST_TABLE MODIFY OBJECT_ID NOT NULL;
再执行COUNT(*)
和COUNT(列)
,结果以下图:
SELECT COUNT(*) FROM TEST_TABLE;
CPU开销:45,递归调用:0,一致性读:168
咱们发现
COUNT(*)
也走索引了,性能也快了不少。
SELECT COUNT(OBJECT_ID) FROM TEST_TABLE;
CPU开销:45,递归调用:0,一致性读:168
而
COUNT(列)
没有太大变化。
条件 | 操做 | 结果 |
---|---|---|
未建索引 | COUNT(*) | 全表扫描 |
未建索引 | COUNT(列) | 全表扫描 |
建索引(索引列能够为空) | COUNT(*) | 全表扫描 |
建索引(索引列能够为空) | COUNT(索引列) | 索引快速扫描 |
建索引(索引列能够为空) | COUNT(非索引列) | 全表扫描 |
建索引(索引列不能为空) | COUNT(*) | 索引快速扫描 |
建索引(索引列不能为空) | COUNT(索引列) | 索引快速扫描 |
总结一句话就是,COUNT(*)
在有索引且索引非空的状况下才会走索引,同时注意COUNT(*)
和COUNT(列)
自己是不等价的,使用前先分析业务场景。
当表有索引且索引非空时,COUNT(*)
和COUNT(1)
和SELECT MAX(ROWNUM) FROM XXX
效果是同样的。