Select count(*)、Count(1)、Count(0)的区别和执行效率比较
记得很早以前就有人跟我说过,在使用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)的区别和执行效率比较的更多相关文章
- 你是一直认为 count(1) 比 count(*) 效率高么?
MySQL count(1) 真的比 count(*) 快么? 反正同事们都是这么说的,我也姑且觉得对吧,那么没有自己研究一下究竟?如果我告诉你他们一样,你信么? 有 Where 条件的 count, ...
- Select count(*)和Count(1)的区别和执行方式
在SQL Server中Count(*)或者Count(1)或者Count([列])或许是最常用的聚合函数.很多人其实对这三者之间是区分不清的.本文会阐述这三者的作用,关系以及背后的原理. ...
- SQL Select count(*)和Count(1)的区别和执行方式及SQL性能优化
SQL性能优化:http://www.cnblogs.com/CareySon/category/360333.html Select count(*)和Count(1)的区别和执行方式 在SQL S ...
- [转帖]Select count(*)和Count(1)的区别和执行方式
Select count(*)和Count(1)的区别和执行方式 https://www.cnblogs.com/VicLiu/p/11672303.html 在SQL Server中Count(*) ...
- Select count(*)和Count(1)的区别和执行方式
在SQL Server中Count(*)或者Count(1)或者Count([列])或许是最常用的聚合函数.很多人其实对这三者之间是区分不清的.本文会阐述这三者的作用,关系以及背后的原理. 往常我经常 ...
- [Oracle] “表中有数据,但select count(*)的结果为0”问题的解决办法
一.问题 今天遇到了一个神奇的问题--表中有数据,但select count(*)的结果为0. 这个问题最初的表现形式是"查询报表没有分页". 最开始还以为是java端的问题.后来 ...
- Count(*)或者Count(1)或者Count([列]) 区别
在SQL 中Count(*)或者Count(1)或者Count([列])或许是最常用的聚合函数.很多人其实对这三者之间是区分不清的.本文会阐述这三者的作用,关系以及背后的原理. 往常我经常会看到一些所 ...
- count(1)、count(*)与count(列名)的执行区别
执行效果: 1. count(1) and count(*) 当表的数据量大些时,对表作分析之后,使用count(1)还要比使用count(*)用时多了! 从执行计划来看,count(1)和coun ...
- 【hive】count() count(if) count(distinct if) sum(if)的区别
表名: user_active_day (用户日活表) 表内容: user_id(用户id) user_is_new(是否新用户 1:新增用户 0:老用户) location_city(用户所在地 ...
随机推荐
- 【搜索】C - Catch That Cow
#include<stdio.h> #include<string.h> struct A{ int state; int step; }queue[]; // 结构体数组用来 ...
- 初学者问题一oracle
问:(待解决)如何将纵向表改成横向表? (待解决)如何实现对大型数据范围差距不大的索引?(建什么索引树)
- python里的字典和集合
一.字典 1.字典的定义 字典是不可变的,是用hash值来存储的.字典的key必须是不可变的(可哈希) dict = {key1:value1 , key2:value2} 2.字典的增删改查 增 直 ...
- 2019.02.09 codeforces gym 100548F. Color(容斥原理)
传送门 题意简述:对n个排成一排的物品涂色,有m种颜色可选. 要求相邻的物品颜色不相同,且总共恰好有K种颜色,问所有可行的方案数.(n,m≤1e9,k≤1e6n,m\le1e9,k\le1e6n,m≤ ...
- 2018.10.23 hdu2476String painter(区间dp)
传送门 一道挺妙的区间dp. 我们先用区间dp求出第一个串为空串时的最小代价. 然后再加入原本的字符更新答案就行了. 代码: #include<bits/stdc++.h> using n ...
- CentOS7 安装可视化脚本安装包Webmin
一.简介 Webmin是一个基于Web的Linux系统管理界面.你就可以通过图形化的方式设置用户账号.Apache.DNS.文件共享等服务. 二.安装 1.下载安装包到本地Windows系统 http ...
- Spring MVC 3 表单中文提交post请求和get请求乱码问题的解决方法
在spring mvc 3.0 框架中,通过JSP页面.HTML页面以POST方式提交表单时,表单的参数传递到对应的servlet后会出现中文显示乱码的问题.解决办法可采用spring自带的过滤技术, ...
- Edifact 95B报文解读
PART 1 INTRODUCTION D100_D.95B PART 2 UNIFORM RULES OF CONDUCT FOR INTERCHANGE PART2_D.ZIP(1) OF TRA ...
- 11-border(边框)
边框 border:边框的意思,描述盒子的边框 边框有三个要素: 粗细 线性样式 颜色 border: solid 如果颜色不写,默认是黑色.如果粗细不写,不显示边框.如果只写线性样式,默认的有上下左 ...
- IT桔子-抓取数据