说明:count(*)统计行数考虑null, count(列)统计行数不考虑null
结论:
1.count(1)和count()一样的,这个可以通过执行计划证明
2.count()/count(列)需要分情况讨论
在没有索引的情况下,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优化>