count(1)/count(*)/count(列)等性能比较

测试环境:oracle11g 
一:先看count(*)与count(列)
--场景1:不存在空值,不建索引, object_id属性可为空;
drop table t purge;
create table t as select * from dba_objects;
--alter table T modify object_id  null;
update t set object_id =rownum ;
set timing on 
set linesize 1000
set autotrace on 

alter system flush shared_pool;
select count(*) from t;
-------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Cost (%CPU)| Time     |
-------------------------------------------------------------------
 0 | SELECT STATEMENT   |      |     1 |   291   (1)| 00:00:04 |
 1 |  SORT AGGREGATE    |      |     1 |            |          |
 2 |   TABLE ACCESS FULL| T    | 65364 |   291   (1)| 00:00:04 |
-----------------------------------------------------------------

Note
-----
   - dynamic sampling used for this statement (level=2)


统计信息
----------------------------------------------------------
        282  recursive calls
          0  db block gets
      86017  consistent gets
          0  physical reads
      70124  redo size
        529  bytes sent via SQL*Net to client
        519  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          5  sorts (memory)
          0  sorts (disk)
          1  rows processed

select count(object_id) from t;
---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
 0 | SELECT STATEMENT   |      |     1 |    13 |   292   (1)| 00:00:04 |
 1 |  SORT AGGREGATE    |      |     1 |    13 |            |          
 2 |   TABLE ACCESS FULL| T    | 65364 |   829K|   292   (1)| 00:00:04 |
---------------------------------------------------------------------------

Note
-----
   - dynamic sampling used for this statement (level=2)


统计信息
----------------------------------------------------------
        302  recursive calls
          0  db block gets
      86019  consistent gets
          0  physical reads
      70124  redo size
        537  bytes sent via SQL*Net to client
        519  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          5  sorts (memory)
          0  sorts (disk)
          1  rows processed
可以发现:此种场景下,count(*)比count(列)性能略好;




场景2: object_id建立索引,object_id属性可为空;
create index idx_object_id on t(object_id);
select count(*) from t;
-------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Cost (%CPU)| Time     |
-------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |   291   (1)| 00:00:04 |
|   1 |  SORT AGGREGATE    |      |     1 |            |          |
|   2 |   TABLE ACCESS FULL| T    | 65364 |   291   (1)| 00:00:04
此时走的是全表扫描

select count(object_id) from t;
---------------------------------------------------------------------------------------
| Id  | Operation             | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |               |     1 |    13 |    50   (2)| 00:00:01 |
|   1 |  SORT AGGREGATE       |               |     1 |    13 |            |          |
|   2 |   INDEX FAST FULL SCAN| IDX_OBJECT_ID | 65364 |   829K|    50   (2)| 00:00:01
此时走的是索引列;不用说,此场景count(列)性能高于count(*)


--场景3: object_id建立索引,object_id属性可为空;
create index idx_object_id on t(object_id);
--将列属性改为非空
alter table T modify object_id  not  null;

select count(*) from t;
--走索引
| Id  | Operation             | Name          | Rows  | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |               |     1 |    50   (2)| 00:00:01 |
|   1 |  SORT AGGREGATE       |               |     1 |            
|   2 |   INDEX FAST FULL SCAN| IDX_OBJECT_ID | 65364 |    50   (2)| 00:00:01 |
-------------------------------------------------------------------------------

Note
-----
   - dynamic sampling used for this statement (level=2)


select count(object_id) from t;
-------------------------------------------------------------------------------
| Id  | Operation             | Name          | Rows  | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |               |     1 |    50   (2)| 00:00:01 |
|   1 |  SORT AGGREGATE       |               |     1 |            
|   2 |   INDEX FAST FULL SCAN| IDX_OBJECT_ID | 65364 |    50   (2)| 00:00:01 |
-------------------------------------------------------------------------------

Note
-----
   - dynamic sampling used for this statement (level=2)
此场景都会走索引,结果一样;


--初步结论,在没有索引的情况下,count(*)略快;
在有索引且索引列属性非空的情况在,一样快;
在有索引且索引列属性可空的情况下,count(列)更快;因为走了索引;


场景4:obejct_id存在null值,这种情况,count(*)跟count(object_id)结果就不一样,没有可比性; 
如果你非要比较,可以自已尝试,count(*)还是略快; 


二:单独看count(列)
这里我们创建25列,每列的统计取1000次统计的平均值
drop table t;
declare
  l_sql varchar2(32767);
begin
  l_sql := 'CREATE TABLE t (';
  for i in 1..25 
  loop
    l_sql := l_sql || 'n' || i || ' NUMBER,';
  end loop;
  l_sql := l_sql || 'pad VARCHAR2(1000)) PCTFREE 10';
  execute immediate l_sql;
end;
/
----将记录还有这个表t中填充
declare
  l_sql varchar2(32767);
begin
  l_sql := 'INSERT INTO t SELECT ';
  for i in 1..25
  loop
    l_sql := l_sql || '0,';
  end loop;
  l_sql := l_sql || 'NULL FROM dual CONNECT BY level <= 10000';
  execute immediate l_sql;
  commit;
end;
/



begin
   dbms_stats.gather_table_stats(ownname=>user, tabname=>'t');
end;

select num_rows, blocks from user_tables where table_name = 'T';
--以下动作观察执行速度,比较发现count(*)最快,count(最大列)最慢
declare
  l_dummy pls_integer;
  l_start pls_integer;
  l_stop pls_integer;
  l_sql varchar2(100);
begin
  l_start := dbms_utility.get_time;
  for j in 1..1000
  loop
    execute immediate 'SELECT count(*) FROM t' into l_dummy;
  end loop;
  l_stop := dbms_utility.get_time;
  dbms_output.put_line((l_stop-l_start)/100);

  for i in 1..25
  loop
    l_sql := 'SELECT count(n' || i || ') FROM t';
    l_start := dbms_utility.get_time;
    for j in 1..1000
    loop
      execute immediate l_sql into l_dummy;
    end loop;
    l_stop := dbms_utility.get_time;
    dbms_output.put_line((l_stop-l_start)/100);
  end loop;
end;
/
看下结果:

这里写图片描述
第一个1.03就是count(),后面的是每列的,可以看到,列越远,查询效率越低,而count(*)列偏移量无关,这就体现了他的优势,
这里也印证了上面的场景1,如果你非要用count(列)统计,那么建议常访问的列放在前面,效率更好哈
当然这里没加索引,加了索引,那么效率就一样了;



三:count(*)与count(常量) 这个是等价的

场景1:全是null
drop table tab purge;
create table tab(object_id varchar(2),ename varchar(2));

insert into tab
select null,null from dual connect by rownum<10000;
alter system flush shared_pool;--清空共享池,每次统计都需要的
select count(*) from tab;

这里写图片描述


select count(1) from tab;

这里写图片描述



场景2: 1万数据

drop table  tab;
create table tab 
as select 'a'||rownum  aa,'b'||rownum bb from dual connect by rownum<=10000;
select count(*) from tab; 

这里写图片描述

select count(1) from tab;

这里写图片描述



场景3:15万数据
drop table  tab;
create table tab 
as select 'a'||rownum  aa,'b'||rownum bb from dual connect by rownum<=150000;
select count(*) from tab; 

这里写图片描述

select count(1) from tab;

这里写图片描述


场景4:15万,有索引,这个都走索引,肯定是一样的
drop table  tab;
create table tab 
as select 'a'||rownum  aa,'b'||rownum bb from dual connect by rownum<150000;
create index  tab_ind  on tab(aa);
alter table tab modify aa not null;
count(1)与count(*)执行计划都为:

这里写图片描述

可以发现:count(1)与count(*)都会统计null值,跟数据量的大小没有关系

附加:
针对count(*)的查询性能优化:
查询时间为:
缓存(/result_cache/)<物化视图<位图索引<普通索引<未建索引,可以自行测试

参考:梁敬彬老师-<收获,不止sql优化>

猜你喜欢

转载自blog.csdn.net/u011165335/article/details/80298404