记得很早以前就有人跟我说过,在使用count的时候要用count(1)而不要用count(*),因为使用count(*)的时候会对所有的列进行扫描,相比而言count(1)不用扫描所有列,所以count(1)要快一些。当时是对这一结论深信不疑,虽然不知道为什么。今天正好有时间研究研究看count(*)和count(1)到底有没有性能差异。测试环境是SQL Server 2005 SP2开发版。

在进行测试之前先建立一些测试的数据,代码如下:

 1 create table test(a int, b varchar(100))
2 go
3
4 declare @n int
5 set @n = 1
6 while @n < 100000
7 begin
8 if @n%3 = 0
9 insert into test values (@n, null)
10 if @n%3 = 1
11 insert into test values (@n, str(@n))
12 if @n%3 = 2
13 insert into test values (@n, 'this is text')
14 set @n = @n+1
15 end

这里先说明一下,为了测试的目的,test表里面是故意没有加索引的。

count(*)与count(1)的对比

现在我们开始验证count(*)和count(1)的区别,验证方法很简单,如果两个语句执行效率不一样的话它们的查询计划肯定会不一样的,我们先执行set showplan_text on打开SQL执行计划显示,然后我们执行相应的SQL语句。

先是count(*):

select count(*) from test

接着count(1):

select count(1) from test

对比下两个执行计划我们可以发现是完全一样的,这也就说明count(*)和count(1)的执行效率是完全一样的,根本不存在所谓的单列扫描和多列扫描的问题。

count(col)与count(*)的对比

同样,我们先看一下两个不同count方式的执行计划。

count(*)的执行计划看上面的例子。

count(b)的执行计划:

select count(b) from test

现在能看到这两个执行计划唯一不同的地方就是COUNT的内容,对于count(*)是"|—Stream Aggregate(DEFINE:([Expr1005]=count(*)))",对于count(b)是"|—Stream Aggregate(DEFINE:([Expr1005]=COUNT([AdventureWorks].[dbo].[test].[b])))",那这两种count方式会不会有什么不一样呢?

让我们先看一下BOL里面对count(*)以及count(col)的说明:

COUNT(*) 返回组中的项数。包括 NULL 值和重复项。

COUNT(ALL expression) 对组中的每一行都计算 expression 并返回非空值的数量。

expression: 除 text、image 或 ntext 以外任何类型的表达式。不允许使用聚合函数和子查询。

* :指定应该计算所有行以返回表中行的总数。

COUNT(*) 不需要任何参数,而且不能与 DISTINCT 一起使用。

COUNT(*) 不需要 expression 参数,因为根据定义,该函数不使用有关任何特定列的信息。

COUNT(*) 返回指定表中行数而不删除副本。它对各行分别计数。包括包含空值的行。

也就是说count(*)只是返回表中行数,因此SQL Server在处理count(*)的时候只需要找到属于表的数据块块头,然后计算一下行数就行了,而不用去读取里面数据列的数据。

而对于count(col)就不一样了,为了去除col列中包含的NULL行,SQL Server必须读取该col的每一行的值,然后确认下是否为NULL,然后在进行计数。因此count(*)应该是比count(col)快的,下面我们来验证一下。

我们通过在同样的条件下,将select count(…) from test执行1000次来看两种count方式是否是一样的:

先看count(*)

declare @n int, @a int
set @n = 1 while @n <= 1000
begin
select @a = count(*) from test
set @n = @n+1
end

接着看count(col)

declare @n int, @a int

set @n = 1

while @n <= 1000

begin

    select @a = count(b) from test

    set @n = @n+1

end

从执行结果可以看出相差还是很大的,count(*)比count(col)快了一倍。

不过因为count(*)和count(col)使用的目的是不一样的,在必须要使用count(col)的时候还是要用的,只是在统计表全部行数的时候count(*)就是最佳的选择了。

另外:这里用到的跑1000次的方法也可以用在比较count(*)和count(1)上,在这里你将得到两个一样的执行时间。

count(col)与count(distinct col)比较

同样,我们先对比一下两个执行计划。

select count(b) from test

select count(distinct b) from test

从执行计划我们可以看到,因为表test没有索引,在执行count(distinct col)的时候是通过Hash Match的方式来查找相同值的行,这显然会耗费大量的CPU,同时我们也可以知道count(col)能比count(distinct col)快很多的。(如果test的列b有索引的话count(distinct col)的方式会不一样,走的是group by,但同样还是会比count(col)慢的,这个大家可以自己试一下)。

我们可以同样做一个执行1000次看花费的时间来做一个直观的对比。

declare @n int, @a int
set @n = 1 while @n <= 1000
begin
select @a = count(b) from test
set @n = @n+1
end
declare @n int, @a int
set @n = 1 while @n <= 1000
begin
select @a = count(distinct b) from test
set @n = @n+1
end

索引与count的关系

我们上面讨论的都是表的索引结构不变的情况下count的变化,在表索引不变时对表做全表扫描所消耗的IO是不变的,不管是采取那种方式。现在在这里我们将看看不同类型的表索引对count会有什么样的变化,因为索引结构的改变对IO影响是最大的,在这里我们注重关注IO的变化情况。

先罗列一下我们要用到的SQL语句,包括查看IO,TIME、执行计划以及建立索引的。

-- 打开IO显示

set statistics io on

-- 打开执行时间显示

set statistics time on

-- 打开执行计划显示

set showplan_text on

-- 建立聚集索引pk_test

create clustered index pk_test on test (a)

-- 建立非聚集索引ix_a

create index ix_a on test (a)

-- 建立非聚集索引ix_b

create index ix_b on test (b)

堆表和聚集索引表上的count(*)

在这里我们先取得test没有建立索引之前执行count(*)的消耗,然后再在test上对a列建立一个聚集索引,然后再看看同样语句的执行计划和IO。

select count(*) from test

从实际测试我们可以看到,堆表和聚集索引表上的count是没有什么区别的,甚至于聚集索引表上的IO还要多2(这是因为多了两个聚集索引的数据块造成的)。如果你对聚集索引的结构很了解的话也是不难解释的:其实聚集索引并没有单独的保留所有索引列的信息,而只是将表中的行的物理顺序按照聚集索引列的顺序整理了一下,因此对聚集索引的扫描和对堆表的扫描是一样的,没有什么本质上的区别。

因此聚集索引对于count来说是没有帮助的。

非聚集索引上的count

现在我们执行前面给出的语句为test表增加一个非聚集索引ix_a然后看看执行计划和IO情况。

select count(*) from test

从执行结果可以看到,逻辑读的次数明显的减少了,因为计算行数这个操作对于全表扫描或是非聚集索引的扫描结果是一样的,而相对来说非聚集索引的数据量是肯定会比表的数据量小很多的,同样的做一次全部扫描所花费的IO也就要少很多了。

同样的对于一个count(col)的操作来说,对col的索引做count同样是能达到count(col)的目的的,相比全表扫描一样可以节省很多的IO操作。

select count(a) from test

结论

这里把上面实验的结果总结一下:

    • count(*)和count(1)执行的效率是完全一样的。
    • count(*)的执行效率比count(col)高,因此可以用count(*)的时候就不要去用count(col)。
    • count(col)的执行效率比count(distinct col)高,不过这个结论的意义不大,这两种方法也是看需要去用。
    • 如果是对特定的列做count的话建立这个列的非聚集索引能对count有很大的帮助。
    • 如果经常count(*)的话则可以找一个最小的col建立非聚集索引以避免全表扫描而影响整体性能。
    • 在不加WHERE限制条件的情况下,COUNT(*)与COUNT(COL)基本可以认为是等价的;
      但是在有WHERE限制条件的情况下,COUNT(*)会比COUNT(COL)快非常多;
    • count(0)=count(1)=count(*)
      1. count(指定的有效值)--执行计划都会转化为count(*)

      2. 如果指定的是列名,会判断是否有null,null不计算

Select count(*)、Count(1)、Count(0)的区别和执行效率比较的更多相关文章

  1. 你是一直认为 count(1) 比 count(*) 效率高么?

    MySQL count(1) 真的比 count(*) 快么? 反正同事们都是这么说的,我也姑且觉得对吧,那么没有自己研究一下究竟?如果我告诉你他们一样,你信么? 有 Where 条件的 count, ...

  2. Select count(*)和Count(1)的区别和执行方式

        在SQL Server中Count(*)或者Count(1)或者Count([列])或许是最常用的聚合函数.很多人其实对这三者之间是区分不清的.本文会阐述这三者的作用,关系以及背后的原理.   ...

  3. SQL Select count(*)和Count(1)的区别和执行方式及SQL性能优化

    SQL性能优化:http://www.cnblogs.com/CareySon/category/360333.html Select count(*)和Count(1)的区别和执行方式 在SQL S ...

  4. [转帖]Select count(*)和Count(1)的区别和执行方式

    Select count(*)和Count(1)的区别和执行方式 https://www.cnblogs.com/VicLiu/p/11672303.html 在SQL Server中Count(*) ...

  5. Select count(*)和Count(1)的区别和执行方式

    在SQL Server中Count(*)或者Count(1)或者Count([列])或许是最常用的聚合函数.很多人其实对这三者之间是区分不清的.本文会阐述这三者的作用,关系以及背后的原理. 往常我经常 ...

  6. [Oracle] “表中有数据,但select count(*)的结果为0”问题的解决办法

    一.问题 今天遇到了一个神奇的问题--表中有数据,但select count(*)的结果为0. 这个问题最初的表现形式是"查询报表没有分页". 最开始还以为是java端的问题.后来 ...

  7. Count(*)或者Count(1)或者Count([列]) 区别

    在SQL 中Count(*)或者Count(1)或者Count([列])或许是最常用的聚合函数.很多人其实对这三者之间是区分不清的.本文会阐述这三者的作用,关系以及背后的原理. 往常我经常会看到一些所 ...

  8. count(1)、count(*)与count(列名)的执行区别

    执行效果: 1.  count(1) and count(*) 当表的数据量大些时,对表作分析之后,使用count(1)还要比使用count(*)用时多了! 从执行计划来看,count(1)和coun ...

  9. 【hive】count() count(if) count(distinct if) sum(if)的区别

    表名: user_active_day (用户日活表) 表内容: user_id(用户id)   user_is_new(是否新用户 1:新增用户 0:老用户) location_city(用户所在地 ...

随机推荐

  1. mybatis学习一 环境搭建

    第一步导入Jar包 第二步配置sqlMapConfig.xml文件,xml文件放在src下面 <?xml version="1.0" encoding="UTF-8 ...

  2. LSTM长短期记忆神经网络模型简介

    LSTM网络也是一种时间递归神经网络,解决RNN的长期依赖关系. RNN模型在训练时会遇到梯度消失或者爆炸的问题,训练时计算和反向传播,梯度倾向于在每一时刻递增或递减,梯度发散到无穷大或者0..... ...

  3. linux安装dpkg安装缺少依赖项的解决

    问题: dpkg: error processing package rxvt:i386 (--install): dependency problems - leaving unconfigured ...

  4. ubuntu安装rubyOnRails

    https://gorails.com/setup/ubuntu/16.04#ruby-rbenv 文章很详细

  5. C++中string的使用

    概述 这篇博文为了记录C++中string的使用,用到一点补充一点. 预备 使用string之前需要包含头文件 #include<iostream> #include<string& ...

  6. ORACLE 导入的问题

    1.导入报错 我将ORACLE12.2 导出的文件,导入到ORACLE12.1 . IMP-00010: 不是有效的导出文件, 标头验证失败 解决办法: 修改 dmp 文件版本,使用UEDITOR打开 ...

  7. iptables说明(转)

    原文:https://www.linuxidc.com/Linux/2016-09/134832.htm 前提基础: 当主机收到一个数据包后,数据包先在内核空间中处理,若发现目的地址是自身,则传到用户 ...

  8. C语言程序设计50例(三)(经典收藏)

    [程序31]题目:请输入星期几的第一个字母来判断一下是星期几,如果第一个字母一样,则继续 判断第二个字母.1.程序分析:用情况语句比较好,如果第一个字母一样,则判断用情况语句或if语句判断第二个字母. ...

  9. windows10; ERROR 1010 (HY000): Error dropping database (can't rmdir './test/', errno: 17);默认数据库位置查找

    1.想要导入数据到一个数据库中,但是,无法导入,同时也无法删除数据库重新建立-----------------------------备份当前数据库 2,分析:很多资料显示说数据库下有异常文件,于是就 ...

  10. js,javascript,打印对象,object

    function writeObj(obj){ var description = ""; for(var i in obj){ var property=obj[i]; desc ...