<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          MySQL:SELECT COUNT 小結

          共 3832字,需瀏覽 8分鐘

           ·

          2021-09-13 18:07

          作者 | 翁智華

          來源 | https://www.jianshu.com/p/4913fdd1e277

          背景

          今天團隊在做線下代碼評審的時候,發(fā)現(xiàn)同學們在代碼中出現(xiàn)了 select count(1) 、 select count(*),和具體的select count(字段)的不同寫法,本著分析的目的在會議室討論了起來,那這幾種寫法究竟孰優(yōu)孰劣呢,我們一起來看一下。

          討論歸納

          先來看看MySQL官方對SELECT COUNT的定義:

          傳送門:https://dev.mysql.com/doc/refman/5.6/en/aggregate-functions.html#function_count

          大概可以分下面這幾個步驟討論。

          COUNT(expr)的分析

          COUNT(expr)函數返回的值是由SELECT語句檢索的行中expr表達式非null的計數值,一個BIGINT的值。如果沒有匹配到數據,COUNT(expr)將返回0,通常有下面這三種用法:

          1、COUNT(字段) 會統(tǒng)計該字段在表中出現(xiàn)的次數,忽略字段為null 的情況。即不統(tǒng)計字段為null 的記錄。

          2、COUNT(*) 則不同,它執(zhí)行時返回檢索到的行數的計數,不管這些行是否包含null值,

          3、COUNT(1)跟COUNT(*)類似,不將任何列是否null列入統(tǒng)計標準,僅用1代表代碼行,所以在統(tǒng)計結果的時候,不會忽略列值為NULL的行。

          如果您正在學習Spring Boot,推薦一個連載多年還在繼續(xù)更新的免費教程:http://blog.didispace.com/spring-boot-learning-2x/

          所以執(zhí)行以下數據會出現(xiàn)這樣的結果(這邊是故意給component字段設置了幾個null值):

          select COUNT(*),COUNT(1),COUNT(component) from worklog;

          歸納如下:

          count(*)包括了所有的列,相當于行數,在統(tǒng)計結果的時候,不會忽略列值為NULL

          count(1)包括了忽略所有列,用1代表代碼行,在統(tǒng)計結果的時候,不會忽略列值為NULL

          count(字段)只包括字段那一列,在統(tǒng)計結果的時候,會忽略列值為null的計數,即某個字段值為NULL時,不統(tǒng)計。

          關于 COUNT(*) 和 COUNT(1)

          先看看COUNT(*),MyISAM 引擎會把一個表的總行數記錄了下來,所以在執(zhí)行 COUNT(*)的時候會直接返回數量,執(zhí)行效率很高。對于InnoDB這樣的事務性存儲引擎, 因為增加了版本控制(MVCC)的原因,同時有多個事務訪問數據并且有更新操作的時候,每個事務需要維護自己的可見性,那么每個事務查詢到的行數也是不同的,所以不能緩存具體的行數,他每次都需要count計算一下所有的行數。

          如果您正在學習Spring Boot,推薦一個連載多年還在繼續(xù)更新的免費教程:http://blog.didispace.com/spring-boot-learning-2x/

          至于 COUNT(1) 和 COUNT(*)有什么區(qū)別呢,根據官網的內容(即上述截圖倒數第二段),兩種實現(xiàn)上其實一樣:

          InnoDB handles SELECT COUNT(*) and SELECT COUNT(1) operations in the same way. There is no performance difference.

          因為COUNT(*) 不care返回值是否為空都會將改行納入計算,所以他count了所有行數,而 COUNT(1) 中的 1 ,則是遇到了行的時候為恒真表達式,所以 COUNT(*) 還是 COUNT(1) 都是對所有的結果集進行 count,他們本質上沒有什么區(qū)別。姑且認為 COUNT(*)≈ COUNT(1)。

          關于COUNT(字段)

          我們再來看看的COUNT(字段),他的查詢就簡單粗暴了,就是進行全表掃描,然后判斷拿到的字段的值是不是為NULL,不為NULL則累加。

          相比COUNT(*),COUNT(字段)多了一個步驟就是判斷所查詢的字段是否為NULL,所以他的性能要比COUNT(*)COUNT(1)慢。

          總結

          綜上,COUNT(1)和 COUNT(*)表示的是直接查詢符合條件的數據庫表的行數。而COUNT(字段)表示的是查詢符合條件的列的值,并判斷不為NULL的行數的累計,效率自然會低一點,

          除了查詢得到結果集有區(qū)別之外,相比COUNT(1) 和 COUNT(字段)來講,COUNT(*)是SQL92定義的標準統(tǒng)計數的語法,是官方提供的標準方案,基于此,MySQL數據庫對他進行過很多優(yōu)化。

          注:SQL92,是數據庫的一個ANSI/ISO標準。它定義了一種語言(SQL)以及數據庫的行為(事務、隔離級別等)。

          下面是對一張具有3400W數據的表的統(tǒng)計過程,comid是整型,可以對比下執(zhí)行效率差異:

          使用建議

          根據總結的內容,從效率層面說,COUNT(*)≈ COUNT(1) > COUNT(字段),又因為 COUNT(*)是SQL92定義的標準統(tǒng)計數的語法,我們建議使用 COUNT(*)。

          我們再來看看MySQL數據庫做了哪些優(yōu)化:以MySQL中比較常用的執(zhí)行引擎InnoDB和MyISAM為例子。

          1、MyISAM不支持事務,MyISAM中的鎖是表級鎖;

          因為MyISAM的鎖是表級鎖,所以同一張表上面的操作是串行執(zhí)行的,MyISAM把表的總行數單獨記錄下來,如果只是使用COUNT(*)對表進行查詢的時候,可以直接返回這個記錄的數值就可以了。

          這樣表中總行數記錄即可提供給COUNT(*)查詢使用,又因MyISAM數據庫是表級鎖,數據庫行數不會被并行修改,所以行數是準確無誤的。

          如果您正在學習Spring Boot,推薦一個連載多年還在繼續(xù)更新的免費教程:http://blog.didispace.com/spring-boot-learning-2x/

          2、InnoDB支持事務,其中大部分操作都是行級鎖。

          這樣就不能愉快的做這種緩存操作了,因為表的行數可能會被并發(fā)修改,緩存記錄下來的總行數就不準確了。

          在InnoDB中,使用COUNT(*)查詢行數的時候,不需要進行掃表,只要獲取記錄行數而已。所以官方在針對InnoDB的 SELECT COUNT(*) FROM語句執(zhí)行過程,會自動選擇一個成本較低的索引進行的話,這樣就可以大大節(jié)省時間。

          InnoDB中索引分為聚簇索引(主鍵索引)和非聚簇索引(非主鍵索引),聚簇索引的葉子節(jié)點中保存的是整行記錄,而非聚簇索引的葉子節(jié)點中保存的是該行記錄的主鍵的值,非聚簇索引要比聚簇索引小很多,MySQL會優(yōu)先選擇最小的非聚簇索引來掃表,這樣可以保證COUNT(*)的最優(yōu)效率。

          當查詢語句中包含WHERE以及GROUP BY條件,會有一些其他的因素影響,所以要綜合考慮。

          判斷數據在否,COUNT怎么用?

          上面那種很獲取COUNT數的場景多用于數據分頁,數據統(tǒng)計的場景,有很多的情況則是直接判斷數據是否存在,這種情況下,其實是不關心有多少數據。但是我們CoreReview的時候還是會很經??吹竭@種做法:

          select COUNT(*) from test_ucsyncdetail where comid > 520;

          int count = testDao.CountByComId(comId);

          if(count>0){

          //存在,則執(zhí)行存在分支的代碼
          }else{

          //不存在,則執(zhí)行存在分支的代碼
          }

          更好的寫法應該是這樣:

          select 1 from test_ucsyncdetail where comid > 520 limit1;

          Object tda = testDao.checkExit(comId);

          if(tda !=null){

          //存在,則執(zhí)行存在分支的代碼
          }else{

          //不存在,則執(zhí)行存在分支的代碼
          }

          規(guī)避了SQL使用COUNT表達式掃表的操作,而是改用SELECT 1 ... LIMIT 1,數據庫查詢時遇到一條就返回,不會再繼續(xù)查找和執(zhí)行,如果存在傳輸回一條結果為1的數據 ,否則為null,業(yè)務代碼中直接判斷是否非空即可

          往期推薦

          幫你朋友進來看看:色情片傷害人體的全過程

          建議被降級降薪員工主動辭職?網友炸了!

          如何輕松搞定CRUD的創(chuàng)建人、修改人、時間等字段的賦值

          架構師必備技能:Maven Archetype生成項目模板

          如何更快地將string轉換成int/long



          喜歡本文歡迎轉發(fā),關注我訂閱更多精彩

          關注我回復「加群」,加入Spring技術交流群

          瀏覽 41
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  色电影网址 | 99久热在线精品视频 | 无码一区二区黑人猛烈视频网站 | 全黄做爰100分钟视频 | 天天躁夜夜躁狠狠躁av麻豆男男 |