12
返回列表 发新帖
楼主: Kenniu

[原创] Oracle Count 函数的实验

[复制链接]
论坛徽章:
8
2009新春纪念徽章
日期:2009-01-04 14:52:28祖国60周年纪念徽章
日期:2009-10-09 08:28:002010新春纪念徽章
日期:2010-03-01 11:07:24ITPUB9周年纪念徽章
日期:2010-10-08 09:32:25ITPUB十周年纪念徽章
日期:2011-11-01 16:23:262013年新春福章
日期:2013-02-25 14:51:24沸羊羊
日期:2015-03-04 14:51:522015年新春福章
日期:2015-03-06 11:57:31
11#
发表于 2009-10-9 12:57 | 只看该作者

回复 #7 Kenniu 的帖子

抱歉,我这里不该用类似的简称,我这里的FTS=full table scan,就是全表扫描的意思。

使用道具 举报

回复
论坛徽章:
138
19周年集字徽章-19
日期:2020-06-08 08:30:56马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有车
日期:2014-02-19 11:55:14马上有房
日期:2014-02-18 16:42:022014年新春福章
日期:2014-02-18 16:42:02路虎
日期:2013-11-22 12:26:18问答徽章
日期:2014-05-08 12:15:31
12#
发表于 2009-10-9 13:05 | 只看该作者
原帖由 sqysl 于 2009-10-9 09:05 发表
这个话题以前记得讨论过,您这里的SELECT COUNT (b.PLANRMK) 查询的不是行数,而是这个字段里值的个数,由于null不是任何值,所以在计数时不作为一个数据来计数,因此产生了您说说的结果,而select(PK),select count(*),select count(1)中,select(PK)是查询的表的主键字段值的个数,因为主键是不重复而且不为空的,所以它和表里记录的个数是一样的;select count(*)就不用说了,就是查询的表的行数;select count(1)这里是查询的第一个字段的值的个数,其实,如果第一个字段存在空值也统计不出行数,甚至会产生您说的结果。至于您说的select(not null column)也是求得的是表的非空字段值的个数,如果不加DISTINCT关键字,也可以是等于表的行数。而且,您例子中的查询走的是FTS。


你怎么知道楼主例子里的走的是fts ???

使用道具 举报

回复
论坛徽章:
138
19周年集字徽章-19
日期:2020-06-08 08:30:56马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有车
日期:2014-02-19 11:55:14马上有房
日期:2014-02-18 16:42:022014年新春福章
日期:2014-02-18 16:42:02路虎
日期:2013-11-22 12:26:18问答徽章
日期:2014-05-08 12:15:31
13#
发表于 2009-10-9 13:06 | 只看该作者
oracle的聚合函数会忽略null

使用道具 举报

回复
论坛徽章:
138
19周年集字徽章-19
日期:2020-06-08 08:30:56马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有车
日期:2014-02-19 11:55:14马上有房
日期:2014-02-18 16:42:022014年新春福章
日期:2014-02-18 16:42:02路虎
日期:2013-11-22 12:26:18问答徽章
日期:2014-05-08 12:15:31
14#
发表于 2009-10-9 13:08 | 只看该作者
原帖由 Kenniu 于 2009-10-9 08:15 发表
以下一点小经验,希望对你们有所帮助。
在做项目的时候,有修改到的procedure中写了类似这样一句
SELECT COUNT (b.PLANRMK) INTO varplantmkcount FROM  smtbcrud b WHERE ...
If varplantmkcount > 0
    SELECT b.PLANRMK into varplantmk FROM  smtbcrud b WHERE ...
    If varplantmk is null
        ...
    End If;
End If;
本意是想如果smtbcrud里面如果找到了匹配的行的话才继续做If里面的东西,而我的If里面做的是判断如果PLANRMK为空的话则update为***.
用意很简单,但是却出现了问题,PLANRMK 为空的话update不了。

大家可以试一下,如果TableA的栏位a的值全部为Null的话,大家用这句Sql试一下:
select count(a) from TableA
结果是多少?TableA的行数(我以前是这样认为的)?错了,结果为0.

原来Count会忽略null的行。
可以再试一个例子,还是TableA的栏位a, 除了一行是有值之外,其他行都是为Null,再用这句Sql:
select count(a) from TableA
想必大家都知道结果了,结果为1,Null的行被忽略掉了。

怎么解决?用select(PK),select count(*),select count(1)或者select(not null column)代替就好了。
当然这些方式在效率上会有一点点差异,但如果不是数据量很大的话,基本上我们可以忽略。如果有可能数据量很大的话,我的愚见是select(PK)>select count(*)>select count(1)>select(not null column), 大家可以上网找一些资料,或者自己做一些测试。
当然你硬要用这个栏位的话,又想不忽略null行的话,你可以试一下
select count(nvl(a,0)) from TableA


count(pk),count(*),count(1)这几个在执行计划一样的情况下,并没有什么区别,至少在现在的版本里,可能历史版本有些区别

如果存在pk,count(*),count(1)一般也会选择index fast full scan(pk index)

使用道具 举报

回复
论坛徽章:
8
2009新春纪念徽章
日期:2009-01-04 14:52:28祖国60周年纪念徽章
日期:2009-10-09 08:28:002010新春纪念徽章
日期:2010-03-01 11:07:24ITPUB9周年纪念徽章
日期:2010-10-08 09:32:25ITPUB十周年纪念徽章
日期:2011-11-01 16:23:262013年新春福章
日期:2013-02-25 14:51:24沸羊羊
日期:2015-03-04 14:51:522015年新春福章
日期:2015-03-06 11:57:31
15#
发表于 2009-10-9 13:19 | 只看该作者

回复 #12 棉花糖ONE 的帖子

因为这个列可以为空,即使上面建有索引且每行有值,在进行COUNT统计时,也不会走这个索引的,除非该列非空。

使用道具 举报

回复
论坛徽章:
138
19周年集字徽章-19
日期:2020-06-08 08:30:56马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有车
日期:2014-02-19 11:55:14马上有房
日期:2014-02-18 16:42:022014年新春福章
日期:2014-02-18 16:42:02路虎
日期:2013-11-22 12:26:18问答徽章
日期:2014-05-08 12:15:31
16#
发表于 2009-10-9 13:24 | 只看该作者
原帖由 sqysl 于 2009-10-9 13:19 发表
因为这个列可以为空,即使上面建有索引且每行有值,在进行COUNT统计时,也不会走这个索引的,除非该列非空。


因为这个count(object_id)本身是忽略空的,索引直接读索引是满足这个的,count(OBJECT_ID)相当于已经隐含了一个object_id not null的条件,聚合函数不统计null,所以恰好构成了index fast full scan的前提


SCOTT@oracle10g>create table kk as select * from dba_objects;

Table created.

SCOTT@oracle10g>create index i_kk_1 on kk(object_id);

Index created.

SCOTT@oracle10g>set autot trace
SCOTT@oracle10g>select count(OBJECT_ID) FROM KK;


Execution Plan
----------------------------------------------------------
Plan hash value: 3759150837

--------------------------------------------------------------------------------
| Id  | Operation             | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |        |     1 |    13 |    30   (4)| 00:00:01 |
|   1 |  SORT AGGREGATE       |        |     1 |    13 |            |          |
|   2 |   INDEX FAST FULL SCAN| I_KK_1 | 50592 |   642K|    30   (4)| 00:00:01 |
--------------------------------------------------------------------------------

Note
-----
   - dynamic sampling used for this statement


Statistics
----------------------------------------------------------
         28  recursive calls
          0  db block gets
        190  consistent gets
        112  physical reads
          0  redo size
        421  bytes sent via SQL*Net to client
        400  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

SCOTT@oracle10g>SET AUTOT OFF
SCOTT@oracle10g>/

COUNT(OBJECT_ID)
----------------
           50482

SCOTT@oracle10g>INSERT INTO KK(object_id) values(null);

1 row created.

SCOTT@oracle10g>commit;

SCOTT@oracle10g>set autot trace
SCOTT@oracle10g>select count(*) from kk;


Execution Plan
----------------------------------------------------------
Plan hash value: 148281040

-------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Cost (%CPU)| Time     |
-------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |   171   (2)| 00:00:03 |
|   1 |  SORT AGGREGATE    |      |     1 |            |          |
|   2 |   TABLE ACCESS FULL| KK   | 55735 |   171   (2)| 00:00:03 |
-------------------------------------------------------------------

Note
-----
   - dynamic sampling used for this statement


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
        717  consistent gets
          0  physical reads
          0  redo size
        413  bytes sent via SQL*Net to client
        400  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

SCOTT@oracle10g>select count(object_id) from kk;


Execution Plan
----------------------------------------------------------
Plan hash value: 3759150837

--------------------------------------------------------------------------------
| Id  | Operation             | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |        |     1 |    13 |    30   (4)| 00:00:01 |
|   1 |  SORT AGGREGATE       |        |     1 |    13 |            |          |
|   2 |   INDEX FAST FULL SCAN| I_KK_1 | 55735 |   707K|    30   (4)| 00:00:01 |
--------------------------------------------------------------------------------

Note
-----
   - dynamic sampling used for this statement


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
        119  consistent gets
          0  physical reads
          0  redo size
        421  bytes sent via SQL*Net to client
        400  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

SCOTT@oracle10g>select count(*) from kk where object_id is not null;


Execution Plan
----------------------------------------------------------
Plan hash value: 3759150837

--------------------------------------------------------------------------------
| Id  | Operation             | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |        |     1 |    13 |    30   (4)| 00:00:01 |
|   1 |  SORT AGGREGATE       |        |     1 |    13 |            |          |
|*  2 |   INDEX FAST FULL SCAN| I_KK_1 | 55735 |   707K|    30   (4)| 00:00:01 |
--------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter("OBJECT_ID" IS NOT NULL)

Note
-----
   - dynamic sampling used for this statement


Statistics
----------------------------------------------------------
          5  recursive calls
          0  db block gets
        193  consistent gets
          0  physical reads
          0  redo size
        413  bytes sent via SQL*Net to client
        400  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

使用道具 举报

回复
论坛徽章:
8
2009新春纪念徽章
日期:2009-01-04 14:52:28祖国60周年纪念徽章
日期:2009-10-09 08:28:002010新春纪念徽章
日期:2010-03-01 11:07:24ITPUB9周年纪念徽章
日期:2010-10-08 09:32:25ITPUB十周年纪念徽章
日期:2011-11-01 16:23:262013年新春福章
日期:2013-02-25 14:51:24沸羊羊
日期:2015-03-04 14:51:522015年新春福章
日期:2015-03-06 11:57:31
17#
发表于 2009-10-9 14:36 | 只看该作者
嗯,你说的是对的。
其实,即使你不往KK里插入OBJECT_ID=NULL数据行,SELECT COUNT(*) FROM KK也会走全表扫的。
SELECT DISTINCT OBJECT_ID FROM KK也是一样。

使用道具 举报

回复
论坛徽章:
138
19周年集字徽章-19
日期:2020-06-08 08:30:56马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有车
日期:2014-02-19 11:55:14马上有房
日期:2014-02-18 16:42:022014年新春福章
日期:2014-02-18 16:42:02路虎
日期:2013-11-22 12:26:18问答徽章
日期:2014-05-08 12:15:31
18#
发表于 2009-10-9 14:38 | 只看该作者
原帖由 sqysl 于 2009-10-9 14:36 发表
嗯,你说的是对的。
其实,即使你不往KK里插入OBJECT_ID=NULL数据行,SELECT COUNT(*) FROM KK也会走全表扫的。
SELECT DISTINCT OBJECT_ID FROM KK也是一样。


yes,这时候oracle无法获得隐式的not null条件,就必须通过not null约束或者object_id is not null条件,才能保证iffs得到正确的结果

使用道具 举报

回复
论坛徽章:
47
蒙奇·D·路飞
日期:2017-03-27 08:04:23马上有车
日期:2014-02-18 16:41:112014年新春福章
日期:2014-02-18 16:41:11一汽
日期:2013-09-01 20:46:27复活蛋
日期:2013-03-13 07:55:232013年新春福章
日期:2013-02-25 14:51:24ITPUB 11周年纪念徽章
日期:2012-10-09 18:03:322012新春纪念徽章
日期:2012-02-13 15:13:202012新春纪念徽章
日期:2012-02-13 15:13:202012新春纪念徽章
日期:2012-02-13 15:13:20
19#
发表于 2009-10-10 04:32 | 只看该作者
> SELECT COUNT (b.PLANRMK) INTO varplantmkcount FROM  smtbcrud b WHERE ...
> If varplantmkcount > 0

This may be a typical question in an interview: How do you improve the above PL/SQL code, suppose smtbcrud is a fairly large table? The answer is simple: add "rownum = 1" to the where clause. (After that, the "If varplantmkcount" check can be either "> 0" or "= 1".) Have you wondered why "grep -l pattern file" is faster than "grep pattern file" if all you want is to know if pattern exists in file and pattern is not near the end of file? It's the same idea.

> 用select(PK),select count(*),select count(1)或者select(not null column)代替就好了。
> 当然这些方式在效率上会有一点点差异

There shouldn't be any difference in performance.

> select count(nvl(a,0)) from TableA

That will be slightly slower because it uses CPU to process nvl.

> select count(1)这里是查询的第一个字段的值的个数

That's not right. select count(1) is actually still counting all rows. If you really want, you can say it's counting how many times each row can be represented by the number 1. Of course every row can. It's like select 1 from mytable. How many rows will that return? All rows in mytable.

Also, when we say an index ignores null, we should qualify that with "B*Tree index", because a bitmap index indexes null values. And in case of a composite B*Tree index, it skips the row only if *all* columns in the index are null.

Yong Huang

[ 本帖最后由 Yong Huang 于 2009-10-9 20:23 编辑 ]

使用道具 举报

回复
论坛徽章:
8
2009新春纪念徽章
日期:2009-01-04 14:52:28祖国60周年纪念徽章
日期:2009-10-09 08:28:002010新春纪念徽章
日期:2010-03-01 11:07:24ITPUB9周年纪念徽章
日期:2010-10-08 09:32:25ITPUB十周年纪念徽章
日期:2011-11-01 16:23:262013年新春福章
日期:2013-02-25 14:51:24沸羊羊
日期:2015-03-04 14:51:522015年新春福章
日期:2015-03-06 11:57:31
20#
发表于 2009-10-10 08:52 | 只看该作者
谢谢YONGHUANG的回复,学习了。

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表