淺談count(1),count(*)和count(column_name)


   最近看到群里有位仁兄,問到關於count(column_name)和count(*),還有count(1)效率和不同點的問題,我記得,在很久之前提到過關於這塊的問題,很多人對怎么用這三個統計都模糊不清的,所以,今天抽個空,自己做個實驗,測試測試這種情況,我測試的思路是從執行效率上和輸出的數據量這兩方面。

  如果有不到之處,還敬請拍磚!!

--建立測試環境
create table test_a
(
a int
)

declare @max int ,@rc int
set @max =10000000
set @rc =1

while @rc<=@max begin insert into test_a select @max set @rc=@rc+1 end --數據量太大,插的時候太慢了,所以只插了8034568條數據,大約半小時,嘻嘻

--沒有NULL值得情況下,有NULL值得情況我會在后面再討論,現在情況為表為單列的且沒有索引的情況

declare @time_first datetime
declare @time_end    datetime
declare @sql    varchar(20)

select @time_first = GETDATE()
select COUNT(1) from test_a
select @time_end=GETDATE()
--print @time_first
set @sql= DATEDIFF(MILLISECOND,@time_first,@time_end)
print @sql

set @time_first =GETDATE()
select COUNT(a) from test_a
set @time_end=GETDATE()
set @sql= DATEDIFF(MILLISECOND,@time_first,@time_end)
print @sql

set @time_first =GETDATE()
select COUNT(*) from test_a
set @time_end=GETDATE()
set @sql= DATEDIFF(MILLISECOND,@time_first,@time_end)
print @sql
/*
在沒有索引的情況下,我多次執行,得到如下結果(這邊就不一一列舉):
第一次:
-----------
8034568

(1 行受影響)

366

-----------
8034568

(1 行受影響)

803

-----------
8034568

(1 行受影響)

400

第二次:
-----------
8034568

(1 行受影響)

360

-----------
8034568

(1 行受影響)

820

-----------
8034568

(1 行受影響)

396

--第三次:

-----------
8034568

(1 行受影響)

386

-----------
8034568

(1 行受影響)

816

-----------
8034568

(1 行受影響)

370

*/

  為了避免偶然性,我再本機上多次執行了,從得出的結果上很容易得出來,count(column_name)從時間上來看的話,是最慢的,count(1)和count(*)時間上是差不多的。
--我們看看他們各自的執行計划,如圖一:


  由上面的圖片可以看的出來,在沒有索引的情況下他們的執行計划是一樣的。
  我們再來看看磁盤盒CPU的開銷情況。

set statistics time on 
go
select COUNT(1) from test_a
select COUNT(a) from test_a
select COUNT(*) from test_a
set statistics time off
go
/*
表 'test_a'。掃描計數 3,邏輯讀取 13572 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。

SQL Server 執行時間:
CPU 時間 = 688 毫秒,占用時間 = 344 毫秒。

-----------
8034568

(1 行受影響)

表 'test_a'。掃描計數 3,邏輯讀取 13572 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。

SQL Server 執行時間:
CPU 時間 = 1562 毫秒,占用時間 = 774 毫秒。

-----------
8034568

(1 行受影響)

表 'test_a'。掃描計數 3,邏輯讀取 13572 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。

SQL Server 執行時間:
CPU 時間 = 688 毫秒,占用時間 = 347 毫秒。
*/

  從上可以看的出來,在沒有索引的情況下,他們的邏輯讀取和物理讀取是一樣的,但在CPU的輸入輸出上不同,所以,可以得出以下結論,在相同條件下,如果表只有一列,且沒有索引的情況下,count(column_name)的開銷是最大的,而count(1)和count(*)區別不是很大,這種區別在將近900W的數據量的情況下是可以忽略的。

  下面我們討論討論如果有索引的情況下是怎么樣的。

  在原來的環境情況下,增加一個列,和一個索引。

--增加一個標識列
alter table test_a
add id int identity(1,1)
--建立一個聚集索引
create clustered index index_id on test_a(id)
 
         
declare @time_first datetime
declare @time_end    datetime
declare @sql    varchar(20)

select @time_first = GETDATE()
select COUNT(1) from test_a
select @time_end=GETDATE()
--print @time_first
set @sql= DATEDIFF(MILLISECOND,@time_first,@time_end)
print N'執行count(1)所花費的時間:'+ @sql


set @time_first =GETDATE()
select COUNT(ID) from test_a
set @time_end=GETDATE()
set @sql= DATEDIFF(MILLISECOND,@time_first,@time_end)
print N'執行count(ID)所花費的時間(其中ID為索引列):'+@sql

set @time_first =GETDATE()
select COUNT(ID) from test_a
set @time_end=GETDATE()
set @sql= DATEDIFF(MILLISECOND,@time_first,@time_end)
print N'執行count(a)所花費的時間(其中a為非索引列):'+@sql

set @time_first =GETDATE()
select COUNT(*) from test_a
set @time_end=GETDATE()
set @sql= DATEDIFF(MILLISECOND,@time_first,@time_end)
print N'執行count(*):'+@sql

/*
第一次:
-----------
8034568

(1 行受影響)

執行count(1)所花費的時間:353

-----------
8034568

(1 行受影響)

執行count(ID)所花費的時間(其中ID為索引列):406

-----------
8034568

(1 行受影響)

執行count(a)所花費的時間(其中a為非索引列):350

-----------
8034568

(1 行受影響)

執行count(*)所花費的時間:370

第二次:
-----------
8034568

(1 行受影響)

執行count(1)所花費的時間:376

-----------
8034568

(1 行受影響)

執行count(ID)所花費的時間(其中ID為索引列):380

-----------
8034568

(1 行受影響)

執行count(a)所花費的時間(其中a為非索引列):353

-----------
8034568

(1 行受影響)

執行count(*):360

第三次:
-----------
8034568

(1 行受影響)

執行count(1)所花費的時間:360

-----------
8034568

(1 行受影響)

執行count(ID)所花費的時間(其中ID為索引列):403

-----------
8034568

(1 行受影響)

執行count(a)所花費的時間(其中a為非索引列):360

-----------
8034568

(1 行受影響)

執行count(*):360
*/

  很奇怪,為什么加了索引之后反而比沒有加索引的快呢?我們在來看看他們各自的執行計划。

  四個的執行計划是一樣的,但是與之前的是有區別的,加了索引之后,無論你是否是指定索引列還是非索引列,它都默認的走索引列。

  我們再看看磁盤以及CPU的開銷情況。

set statistics time on  
go
select COUNT(1) from test_a
select COUNT(ID) from test_a
select COUNT(a) from test_a
select COUNT(*) from test_a
set statistics time off
go
/*
SQL Server 分析和編譯時間: 
   CPU 時間 = 0 毫秒,占用時間 = 0 毫秒。

-----------
8034568

(1 行受影響)

表 'test_a'。掃描計數 3,邏輯讀取 17021 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。

 SQL Server 執行時間:
   CPU 時間 = 702 毫秒,占用時間 = 348 毫秒。

-----------
8034568

(1 行受影響)

表 'test_a'。掃描計數 3,邏輯讀取 17021 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。

 SQL Server 執行時間:
   CPU 時間 = 720 毫秒,占用時間 = 403 毫秒。

-----------
8034568

(1 行受影響)

表 'test_a'。掃描計數 3,邏輯讀取 17021 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。

 SQL Server 執行時間:
   CPU 時間 = 1624 毫秒,占用時間 = 825 毫秒。

-----------
8034568

(1 行受影響)

表 'test_a'。掃描計數 3,邏輯讀取 17021 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。

 SQL Server 執行時間:
   CPU 時間 = 782 毫秒,占用時間 = 394 毫秒。
*/

  從上面的磁盤和CPU的開銷來看,如果是多列有索引的情況下,並非count(索引列)最快,反而最慢,當然其中也不含有null統計的情況。

 

  下面我們來討論,有NULL值得情況下,數據量的問題。

--數據准備,執行如下語句:
update test_a
set a=null
where id %5=0

select count(*) from test_a
/*
-----------
8034568

(1 行受影響)
*/
select COUNT(*) from test_a
where A is null
/*
--為空的數據為
-----------
1606913

(1 行受影響)

*/

  數據環境出來了,我們來做個統計。

--我們執行以下語句
select COUNT(1) from test_a
select COUNT(*) from test_a
select COUNT(id) from test_a
select COUNT(a) from test_a

/*
-----------
8034568

(1 行受影響)


-----------
8034568

(1 行受影響)


-----------
8034568

(1 行受影響)


-----------
6427655
警告: 聚合或其他 SET 操作消除了 Null 值。

(1 行受影響)
*/

  我們不忙分析,請看下面的執行,能更加加固我們對count()的用法的意思。

--我們在原來的基礎上執行以下語句,建立環境。
begin tran
drop index test_a.index_id-- 刪除索引

alter table test_a
drop column id            --刪除列
commit
--我們再次執行上面的查詢語句:
select COUNT(1) from test_a
select COUNT(*) from test_a
--select COUNT(id) from test_a
select COUNT(a) from test_a

/*
-----------
8034568

(1 行受影響)


-----------
8034568

(1 行受影響)


-----------
6427655
警告: 聚合或其他 SET 操作消除了 Null 值。

(1 行受影響)
*/

  到此為止,我們應該清晰的明白了count()的用法了吧。

  以上僅一家之言,如有不對的地方請各位指教,若有轉載,請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM