原文作者:Jonathan lewis
原文地址:https://jonathanlewis.wordpress.com/2020/02/25/count-again-2/
count(*) - again
-Jonathan Lewis 2020年2月25日下午1:24
我刚刚收到一封电子邮件,问了一个关于计算表中行数的问题。
我们有一个带有CLOB字段的大表,其中CLOB占用85%的存储空间。
当我们使用select count(*) from时,DBA说你不应该使用count(*),因为它会使用这个表的所有列包括CLOB字段,这会导致很高的CPU使用率,而如果使用count(rowid),会更快地查询出相同的结果。
我曾经不止一次地指出,count(*)、count(1)、count(声明为非空列)都将执行与count(*)相同的操作;我也列出了一些古怪的反常现象,也不止一次。然而,count(rowid)有一点不同,它没有被转化为count(*),我们可以从两个证据中看到:
例 A: 10053 (CBO) trace文件的一部分截取
----- Current SQL Statement for this session (sql_id=d381q70418ugs) -----
select count(rowid) from emp1
...
Final query after transformations:******* UNPARSED QUERY IS *******
SELECT COUNT("EMP1".ROWID) "COUNT(ROWID)" FROM "TEST_USER"."EMP1" "EMP1"
与转换为count(*)的各种count()调用不同,count rowids似乎不需要经过CNT(转换为count(*))转换,查询的最终状态仍然显示count(rowid)作为最终步骤。
例B: Column Projection Information的不同
SQL> explain plan for select count(1) from emp1;
SQL> select * from table(dbms_xplan.display(null,null,'projection'));
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------
Plan hash value: 2110459082
-----------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-----------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 7 (15)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | INDEX FAST FULL SCAN| E1_PK | 20000 | 7 (15)| 00:00:01 |
-----------------------------------------------------------------------
Column Projection Information (identified by operation id):
-----------------------------------------------------------
1 - (#keys=0) COUNT(*)[22]
SQL> explain plan for select count(rowid) from emp1;
SQL> select * from table(dbms_xplan.display(null,null,'projection'));
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------
Plan hash value: 2110459082
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 12 | 7 (15)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 12 | | |
| 2 | INDEX FAST FULL SCAN| E1_PK | 20000 | 234K| 7 (15)| 00:00:01 |
-------------------------------------------------------------------------------
Column Projection Information (identified by operation id):
-----------------------------------------------------------
1 - (#keys=0) COUNT(ROWID)[22]
2 - ROWID[ROWID,10]
count(1)查询转换为count(*),我们没有看到任何列信息在步骤2生成并传递给父操作。但是,在count(rowid)中,我们看到Oracle在步骤2中通过块地址和行目录构造实际的rowid,并将它们传递给父操作。
构造rowid并传递所消耗的CPU跟访问数据块相比是微不足道的。所以一开始我怀疑count(1)(或count(*))和count(rowid)之间是否有许多明显的时钟差异,但技术上看来count(rowid)会比任何经过count(*)转换的count要慢也更耗资源。
顺带,count(1)例子的执行计划也告诉我们,count(*)没有“使用所有列”-毕竟,不是大部分表,每一列都在主键里,emp1也不例外,执行计划是对主键索引进行快速全扫。




