0
点赞
收藏
分享

微信扫一扫

Oracle和PG的count


曾经测试过Oracle 11g下count(*)、count(1)、count(主键列)和count(包含空值的列)这几种操作,究竟有何区别,结论如下,

11g下,通过实验结论,说明了count(1)和count(主键索引字段)其实都是执行的count(*),而且会选择索引的FFS扫描方式,count(包含空值的列)这种方式一方面会使用全表扫描,另一方面不会统计空值,因此有可能和业务上的需求就会有冲突,因此使用count统计总量的时候,要根据实际业务需求,来选择合适的方法,避免语义不同。


前几天,碰巧看见PostgreSQL中文社区发的一篇文章,关于在PG中count(1)和count(*)的效率问题,从结论看,和Oracle很像,但是他是从开源code,探究的整个过程,能够更准确地了解背后的原理,

写入count(1)与count(*)是相同的效果。

但是count(id)有些不同,它只计算id是NOT NULL的行数。

因此避免count(*)没有任何用处,反而count(*)的速度还会更快。


源码解读

  •     查看count函数定义

test=# select proname, pronargs, prosrc from pg_proc where proname='count';proname | pronargs | prosrc ---------+----------+-----------------count | 0 | aggregate_dummycount | 1 | aggregate_dummy(2 rows)


  • 什么? 0个参数的count?

test=# select count() from lzzhang;ERROR:  count(*) must be used to call a parameterless aggregate function

执行报错,说好的0个参数呢?


查看语法,仅保留主要代码。



* (*) - normal agg with no args * (aggr_arg,...) - normal agg with args * (ORDER BY aggr_arg,...) - ordered-set agg with no direct args


* (aggr_arg,... ORDER BY aggr_arg,...) - ordered-set agg with direct args * * The zero-argument case is spelled with '*' for consistency with COUNT(*).aggr_args:  '(' '*' ')'                {                    $$ = list_make2(NIL, makeInteger(-1));                }

count(*)的*唯一的作用仅仅是作为count函数0个参数的标识使用!


  •      count查询

我们来看看几种count的情况,



test=# select * from lzzhang;id | id1 | id2 ----+-----+-----1 | | 1 | 1 | 1 | 1 | 12 | | 
 
 
 2 | | 3 | | 3 | | 3 | | (8 rows)
test=# select count(*) from lzzhang;count -------8(1 row)
test=# select count(1) from lzzhang;count -------8(1 row)
 
 
 
 test=# select count('const_string') from lzzhang; count ------- 8(1 row)
test=# select count(id) from lzzhang; count ------- 8(1 row)
test=# select count(id1) from lzzhang; count ------- 2(1 row)


这里我们简单分成三种类型的count

1. 列名(id/id1)-只计算非null的数据

2. 无参(*)–计算全部数据

3. 常量(1/const_string)–计算全部数据

count只计算非null的数据。

三种方式在ExecInterpExpr函数中的处理

列名: EEO_CASE(EEOP_OUTER_FETCHSOME) { slot_getsomeattrs(outerslot, op->d.fetch.last_var);
 EEO_NEXT(); } 只计算非null的数据
常量: EEO_CASE(EEOP_CONST) { *op->resnull = op->d.constval.isnull; 
 
 
  *op->resvalue = op->d.constval.value;
 EEO_NEXT(); } 常量当然不会是null,所以1/const_string会计算全部数据。
无参: EEO_CASE(EEOP_AGG_STRICT_TRANS_CHECK) { AggState *aggstate; AggStatePerGroup pergroup;
 aggstate = op->d.agg_strict_trans_check.aggstate; pergroup = &aggstate->all_pergroups [op->d.agg_strict_trans_check.setoff] [op->d.agg_strict_trans_check.transno];
if (unlikely(pergroup->transValueIsNull)) 
 
 
 EEO_JUMP(op->d.agg_strict_trans_check.jumpnull);
EEO_NEXT(); } 检查之后就直接计算。


所以*并不会比1快,反而*比1会减少cpu的计算,速度更快!现在cpu的计算速度很快了,我的单核每秒可以计算6.5亿次,所以*和1的时间几乎是一样的。


可见,Oracle和PG对于一些操作,其实存在相同之处的,谈不上谁借鉴,可能更多地还是针对场景,为了满足业务以及性能方面的需求,提供的合理逻辑。其实我想说的是,无论是从10046,还是从PG的源码,我们应该看得深入一些,从原理设计层面,站在架构师、开发人员的视角,了解这个功能,设计的初衷,这才有助于我们借鉴好的设计,学以致用,丰富我们的经验,实现自我提高。

举报

相关推荐

0 条评论