【Oracle性能優化】COUNT(*)和COUNT(列)相比較

本文基於 Oracle 11g版本進行演示sql

或許咱們平時有這麼一些疑問,當你想統計一個查詢的總記錄數時,第一時間就想到count(*)來實現,但忽然又擔憂該表數據量大時,擔憂count(*)性能會不好,因而乎,咱們可能會使用count(列)進行統計,那到底這兩個有什麼區別呢?緩存

1、當表沒有創建索引時

一、首先,咱們在sqlplus執行下面這個腳本:
-- 若是存在,則刪除該表
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,一致性讀:1097post

第二次:性能

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_TABLESELECT COUNT(列) FROM TEST_TABLE是不等價的,若是你拿SELECT COUNT(列) FROM XXX去完成一個統計表記錄數的需求,那GG。

2、當表創建索引時

上面實驗證實了,在沒有索引的前提下,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,一致性讀:168COUNT(列)沒有太大變化。

3、特性總結

條件 操做 結果
未建索引 COUNT(*) 全表掃描
未建索引 COUNT(列) 全表掃描
建索引(索引列能夠爲空) COUNT(*) 全表掃描
建索引(索引列能夠爲空) COUNT(索引列) 索引快速掃描
建索引(索引列能夠爲空) COUNT(非索引列) 全表掃描
建索引(索引列不能爲空) COUNT(*) 索引快速掃描
建索引(索引列不能爲空) COUNT(索引列) 索引快速掃描

總結一句話就是,COUNT(*)在有索引且索引非空的狀況下才會走索引,同時注意COUNT(*)COUNT(列)自己是不等價的,使用前先分析業務場景。

當表有索引且索引非空時,COUNT(*)COUNT(1)SELECT MAX(ROWNUM) FROM XXX效果是同樣的。

相關文章
相關標籤/搜索