SQL,count(*)与count(列)

1. 准备

create table t as select * from dba_objects;

2. 无index


2.1 select (*) from t;

---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |       |       |   324 (100)|          |
|   1 |  SORT AGGREGATE    |      |     1 |    13 |            |          |
|   2 |   TABLE ACCESS FULL| T    |   173K|  2197K|   324   (1)| 00:00:06 |
---------------------------------------------------------------------------

2.2 select count(object_id) from t;

---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |       |       |   324 (100)|          |
|   1 |  SORT AGGREGATE    |      |     1 |    13 |            |          |
|   2 |   TABLE ACCESS FULL| T    |   173K|  2197K|   324   (1)| 00:00:06 |
---------------------------------------------------------------------------

总结:
无INDEX的时候,count(*)和count(列) 走全表扫描,是一样的。

但是在消耗的时间的问题上,如果执行次数如够多,就会发现,
count(*)最快,count(列)的执行速度会随着列的偏移位置而越来越慢!

这是因为优化品的cost算法是和列偏移量有关的。列越靠后,性能越低
count(*)和列偏移量无关,所以性能最佳(参见,收获不至sql优化,17章)


3. 有index,但列值没有指定非空,且没有空值的情况


create index idx_t_1 on t(object_id)


3.1 select count(*) from t;

---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |       |       |   324 (100)|          |
|   1 |  SORT AGGREGATE    |      |     1 |    13 |            |          |
|   2 |   TABLE ACCESS FULL| T    |   173K|  2197K|   324   (1)| 00:00:06 |
---------------------------------------------------------------------------


3.2 select count(object_id) from t

---------------------------------------------------------------------------------
| Id  | Operation             | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |         |       |       |    57 (100)|          |
|   1 |  SORT AGGREGATE       |         |     1 |    13 |            |          |
|   2 |   INDEX FAST FULL SCAN| IDX_T_1 |   173K|  2197K|    57   (0)| 00:00:02 |
---------------------------------------------------------------------------------

COUNT(OBJECT_ID)
----------------
  196954
  
4 有index,列没有指定非空,且有空值的情况


update t set object_id = null where rownum < 1000;

4.1 select count(*) from t;
-------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Cost (%CPU)| Time     |
-------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |       |   324 (100)|          |
|   1 |  SORT AGGREGATE    |      |     1 |            |          |
|   2 |   TABLE ACCESS FULL| T    |   173K|   324   (1)| 00:00:06 |
-------------------------------------------------------------------
COUNT(OBJECT_ID)
----------------
  196954

4.2 select count(object_id) from t;
---------------------------------------------------------------------------------
| Id  | Operation             | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |         |       |       |    57 (100)|          |
|   1 |  SORT AGGREGATE       |         |     1 |    13 |            |          |
|   2 |   INDEX FAST FULL SCAN| IDX_T_1 |   173K|  2197K|    57   (0)| 00:00:02 |
---------------------------------------------------------------------------------
COUNT(OBJECT_ID)
----------------
  195955
  
count(列)会快,但是,可以看到结果两个不一样,少999
count(*)和count(object_id)不等价,所以这种情况要慎重

4.3 在其他非索引,但有空值的列上使用count()

update t set status = null where rownum < 2000;

select count(status) from t;
---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |       |       |   324 (100)|          |
|   1 |  SORT AGGREGATE    |      |     1 |     5 |            |          |
|   2 |   TABLE ACCESS FULL| T    |   173K|   845K|   324   (1)| 00:00:06 |
---------------------------------------------------------------------------

COUNT(STATUS)
-------------
   194958
可以看出结果少了1999,可以看出count(列),不统计空值

5. 有index,列非空的情况


update t set object_id = rownum;
alter table t modify object_id not null;

5.1 select count(*) from t;
-------------------------------------------------------------------------
| Id  | Operation             | Name    | Rows  | Cost (%CPU)| Time     |
-------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |         |       |   124 (100)|          |
|   1 |  SORT AGGREGATE       |         |     1 |            |          |
|   2 |   INDEX FAST FULL SCAN| IDX_T_1 |   173K|   124   (0)| 00:00:03 |
-------------------------------------------------------------------------

5.2 select count(object_id) from t;
-------------------------------------------------------------------------
| Id  | Operation             | Name    | Rows  | Cost (%CPU)| Time     |
-------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |         |       |   124 (100)|          |
|   1 |  SORT AGGREGATE       |         |     1 |            |          |
|   2 |   INDEX FAST FULL SCAN| IDX_T_1 |   173K|   124   (0)| 00:00:03 |
-------------------------------------------------------------------------

可以看出,两种情况一样,都会走索引

5.3 select count(status) from t;
---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |       |       |   324 (100)|          |
|   1 |  SORT AGGREGATE    |      |     1 |     5 |            |          |
|   2 |   TABLE ACCESS FULL| T    |   173K|   845K|   324   (1)| 00:00:06 |
---------------------------------------------------------------------------
count(列),列为非索引列时,走全表扫描



总结:
各种情况下,与count(列)与count(*)比较
+--------------------------------------+-----------------------+---------------------------+
|                                                          |  count(索引列)           |  count(非索引列)              |
+--------------------------------------+-----------------------+---------------------------+
|无索引、无空值                                  |   --------                    | 等价,count(*)快,见2.2   |
|无索引、有空值                                  |   --------                    | 不等价                               |
|有索引、列没指定NOT NULL、无空值| 等价、比count(*)快    | 等价、一样快                    |
|有索引、列没指定NOT NULL、有空值| 不等价                        | 不等价                              |
|有索引、列指定NOT NULL                 | 等价、一样快              | 等价、count(*)快,见5.3  |

       +--------------------------------------+------------------------+---------------------------+


6. 扩展


组合索引,且count(列)为索引前列

drop index idx_t_1;
create index idx_t_1 on t(object_id,object_type);

6.1 select count(*) from t;
-------------------------------------------------------------------------
| Id  | Operation             | Name    | Rows  | Cost (%CPU)| Time     |
-------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |         |       |    83 (100)|          |
|   1 |  SORT AGGREGATE       |         |     1 |            |          |
|   2 |   INDEX FAST FULL SCAN| IDX_T_1 |   173K|    83   (0)| 00:00:02 |
-------------------------------------------------------------------------

6.2 select count(object_id) from t;
-------------------------------------------------------------------------
| Id  | Operation             | Name    | Rows  | Cost (%CPU)| Time     |
-------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |         |       |    83 (100)|          |
|   1 |  SORT AGGREGATE       |         |     1 |            |          |
|   2 |   INDEX FAST FULL SCAN| IDX_T_1 |   173K|    83   (0)| 00:00:02 |
-------------------------------------------------------------------------

6.3 select count(object_type) from t;

-------------------------------------------------------------------------
| Id  | Operation             | Name    | Rows  | Cost (%CPU)| Time     |
-------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |         |       |    83 (100)|          |
|   1 |  SORT AGGREGATE       |         |     1 |            |          |
|   2 |   INDEX FAST FULL SCAN| IDX_T_1 |   173K|    83   (0)| 00:00:02 |
-------------------------------------------------------------------------

可以看出,对于索引快速全扫描,索引前列和索引后列,其他没有啥差别

至于相同的查询条件为什么前面的cost为124,后面的为83

因为gather_table_stat收集了下表的统计信息,所以后面cost更准确一些

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值