本文基於 Oracle 11g版本進行演示sql
或許咱們平時有這麼一些疑問,當你想統計一個查詢的總記錄數時,第一時間就想到count(*)
來實現,但忽然又擔憂該表數據量大時,擔憂count(*)
性能會不好,因而乎,咱們可能會使用count(列)
進行統計,那到底這兩個有什麼區別呢?緩存
-- 若是存在,則刪除該表
DROP TABLE TEST_TABLE;
-- 基於DBA_OBJECTS創建一張測試表,這張表是沒有任何主鍵、外鍵、索引的
CREATE TABLE TEST_TABLE AS (SELECT * FROM DBA_OBJECTS);
複製代碼
咱們獲得一個TEST_TABLE
表,用於咱們本身測試,該表派生於DBA_OBJECTS
表,注意只有sys
用戶才能使用這張表,請使用sys
進行登陸。性能優化
SELECT COUNT(*) FROM TEST_TABLE;
bash
第一次:oracle
CPU開銷:281,遞歸調用:28,一致性讀:1097
post
第二次:性能
CPU開銷:281,遞歸調用:0,一致性讀:1031
這裏你會發現,開銷同樣,可是遞歸調用竟然變成0,一致性讀也變少了不少,爲何呢?由於oracle執行完請求以後會將數據緩存到cache中,對應oracle的share pool區域測試
SELECT COUNT(OBJECT_ID) FROM TEST_TABLE;
優化
第一次:spa
CPU開銷:281,遞歸調用:27,一致性讀:1097
第二次:
CPU開銷:281,遞歸調用:0,一致性讀:1031
你會發現,其實COUNT(*)
和COUNT(OBJECT_ID)
的效率是同樣快的。你可能會不服氣,你幹嗎選OBJECT_ID
做爲演示,你乾脆換個別的列看看,好的,下面演示別的列:
SELECT COUNT(STATUS) FROM TEST_TABLE;
第一次:
CPU開銷:281,遞歸調用:4,一致性讀:1095
第二次:
CPU開銷:281,遞歸調用:0,一致性讀:1031
結果仍是1031個一致性讀
,同樣快啊~我仍是不信,你選擇有不少空值的列SUBOBJECT_NAME
這個列進行演示吧!好,看下面腳本: SELECT COUNT(SUBOBJECT_NAME) FROM TEST_TABLE;
第一次:
CPU開銷:281,遞歸調用:4,一致性讀:1095
第二次:
CPU開銷:281,遞歸調用:0,一致性讀:1031
是吧!仍是同樣,一致性讀都是1031
,效率是沒有差異的。可是,你發現沒有,最後兩張圖我格外圈了個小圈圈,總條數怎麼變成357了呢?不該該是72056行嗎? 這是因爲COUNT(列)
在統計時,對於該列的值如果爲null,則不參加計算,這是說明,其實SELECT COUNT(*) FROM TEST_TABLE
和SELECT COUNT(列) FROM TEST_TABLE
是不等價的,若是你拿SELECT COUNT(列) FROM XXX
去完成一個統計表記錄數的需求,那GG。
上面實驗證實了,在沒有索引的前提下,COUNT(*)
和COUNT(列)
是沒有差異的。假設咱們給OBJECT_ID
這個列加上一個索引會怎樣,執行下面腳本: CREATE INDEX OBJECT_ID_INDEX ON TEST_TABLE(OBJECT_ID);
而後咱們再執行下面腳本:
SELECT COUNT(*) FROM TEST_TABLE;
第二遍:
CPU開銷:281,遞歸調用:0,一致性讀:1031
從上圖能夠看出,即便咱們創建了索引,COUNT(*)
也不會走索引,反而從執行計劃能夠看出走了全表掃描TABLE ACCESS FULL
,關於執行計劃的相關內容,可閱讀小編另一篇文章【Oracle性能優化一】執行計劃與索引類型分析
那麼此時COUNT(OBJECT_ID)
效果如何呢?執行下面腳本: SELECT COUNT(OBJECT_ID) FROM TEST_TABLE;
CPU開銷:45,遞歸調用:0,一致性讀:168
第二遍發現CPU開銷和一致性讀都變少了不少,並且還走了索引掃描INDEX FAST FULL SCAN
,性能確實了得。固然了,這是針對索引列的COUNT(索引列)
,非索引列效果如何呢?執行下面腳本:
SELECT COUNT(SUBOBJECT_NAME) FROM TEST_TABLE;
CPU開銷:281,遞歸調用:0,一致性讀:1031
發現COUNT(非索引列)
沒有太大變化。有個小問題,咱們那個OBJECT_ID
是容許爲空的,假設非空會怎樣呢?執行下面腳本:
ALTER TABLE TEST_TABLE MODIFY OBJECT_ID NOT NULL;
再執行COUNT(*)
和COUNT(列)
,結果以下圖:
SELECT COUNT(*) FROM TEST_TABLE;
CPU開銷:45,遞歸調用:0,一致性讀:168
咱們發現
COUNT(*)
也走索引了,性能也快了不少。
SELECT COUNT(OBJECT_ID) FROM TEST_TABLE;
CPU開銷:45,遞歸調用:0,一致性讀:168
而
COUNT(列)
沒有太大變化。
條件 | 操做 | 結果 |
---|---|---|
未建索引 | COUNT(*) | 全表掃描 |
未建索引 | COUNT(列) | 全表掃描 |
建索引(索引列能夠爲空) | COUNT(*) | 全表掃描 |
建索引(索引列能夠爲空) | COUNT(索引列) | 索引快速掃描 |
建索引(索引列能夠爲空) | COUNT(非索引列) | 全表掃描 |
建索引(索引列不能爲空) | COUNT(*) | 索引快速掃描 |
建索引(索引列不能爲空) | COUNT(索引列) | 索引快速掃描 |
總結一句話就是,COUNT(*)
在有索引且索引非空的狀況下才會走索引,同時注意COUNT(*)
和COUNT(列)
自己是不等價的,使用前先分析業務場景。
當表有索引且索引非空時,COUNT(*)
和COUNT(1)
和SELECT MAX(ROWNUM) FROM XXX
效果是同樣的。