二十六、COUNT(*)與COUNT(列)到底誰更快?
阿新 • • 發佈:2018-12-10
COUNT(*)與COUNT(列)到底誰更快?
*count(列)當列值為空,將不被統計。
1、資料準備
--做個試驗,看看到底誰更快? drop table t purge; create table t as select * from dba_objects; update t set object_id =rownum ; --設定執行計劃自動跟蹤(sqlplus) set timing on set linesize 1000 set autotrace on
2、COUNT(*)與COUNT(列)沒有索引下的執行計劃
沒建索引一樣快
select count(*) from t;
SQL> set autotrace on SQL> select count(*) from t; COUNT(*) ---------- 72670 執行計劃 ---------------------------------------------------------- Plan hash value: 2966233522 ------------------------------------------------------------------- | Id | Operation | Name | Rows | Cost (%CPU)| Time | ------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 290 (1)| 00:00:04 | | 1 | SORT AGGREGATE | | 1 | | | | 2 | TABLE ACCESS FULL| T | 85840 | 290 (1)| 00:00:04 | ------------------------------------------------------------------- Note ----- - dynamic sampling used for this statement (level=2) 統計資訊 ---------------------------------------------------------- 4 recursive calls 0 db block gets 1119 consistent gets 0 physical reads 0 redo size 529 bytes sent via SQL*Net to client 519 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed
select count(object_id) from t;
SQL> select count(object_id) from t; COUNT(OBJECT_ID) ---------------- 72670 執行計劃 ---------------------------------------------------------- Plan hash value: 2966233522 --------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 13 | 291 (1)| 00:00:04 | | 1 | SORT AGGREGATE | | 1 | 13 | | | | 2 | TABLE ACCESS FULL| T | 85840 | 1089K| 291 (1)| 00:00:04 | --------------------------------------------------------------------------- Note ----- - dynamic sampling used for this statement (level=2) 統計資訊 ---------------------------------------------------------- 4 recursive calls 0 db block gets 1119 consistent gets 0 physical reads 0 redo size 537 bytes sent via SQL*Net to client 519 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed
引數分析:
引數 | 解釋 |
---|---|
recursive calls | 在使用者和系統級生成的遞迴呼叫的數目 |
db block gets | 當前方式從緩衝區高速緩衝中讀取的 總塊數 |
consistent gets | 資料請求總數在回滾段Buffer中的資料一致性讀所需要的資料塊 |
physical reads | 資料請求總數在回滾段Buffer中的資料一致性讀所需要的資料塊 |
redo size | 該操作產生的redo的數量,其單位為Bytes |
bytes sent via SQL*Net to client | 通過SQL*NET傳送給客戶端的位元組 |
bytes received via SQL*Net from client | 通過SQL*NET接收給客戶端的位元組 |
SQL*Net roundtrips to/from client | SQL*Net往返行程 |
sorts (memory) | 在 SORT_AREA_SIZE 中的排序操作的數量 |
sorts (disk) | 在磁碟上執行的排序量 |
rows processed | 執行的行數 |
3、為列新增索引後COUNT(*)與COUNT(列)
為列object_id建立索引,COUNT(列)快
SQL> create index idx_object_id on t(object_id);
索引已建立。
SQL> set autotrace on
SQL> select count(*) from t;
COUNT(*)
----------
72670
執行計劃
----------------------------------------------------------
Plan hash value: 2966233522
-------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 290 (1)| 00:00:04 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| T | 72670 | 290 (1)| 00:00:04 |
-------------------------------------------------------------------
統計資訊
----------------------------------------------------------
1 recursive calls
0 db block gets
1040 consistent gets
0 physical reads
0 redo size
529 bytes sent via SQL*Net to client
519 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
SQL> select count(object_id) from t;
COUNT(OBJECT_ID)
----------------
72670
執行計劃
----------------------------------------------------------
Plan hash value: 1131838604
-------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 46 (1)| 00:00:04 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| T | 72670 | 46 (1)| 00:00:04 |
-------------------------------------------------------------------
統計資訊
----------------------------------------------------------
1 recursive calls
0 db block gets
169 consistent gets
161 physical reads
0 redo size
537 bytes sent via SQL*Net to client
519 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
4、設定列為not null
count(object_id)和count(*)一樣快。
SQL> alter table T modify object_id not null;
表已更改。
SQL> select count(*) from t;
COUNT(*)
----------
72670
執行計劃
----------------------------------------------------------
Plan hash value: 1131838604
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 46 (3)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | INDEX FAST FULL SCAN| IDX_OBJECT_ID | 72670 | 46 (3)| 00:00:01 |
-------------------------------------------------------------------------------
統計資訊
----------------------------------------------------------
208 recursive calls
0 db block gets
199 consistent gets
0 physical reads
0 redo size
529 bytes sent via SQL*Net to client
519 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
6 sorts (memory)
0 sorts (disk)
1 rows processed
SQL> select count(object_id) from t;
COUNT(OBJECT_ID)
----------------
72670
執行計劃
----------------------------------------------------------
Plan hash value: 1131838604
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 46 (3)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | INDEX FAST FULL SCAN| IDX_OBJECT_ID | 72670 | 46 (3)| 00:00:01 |
-------------------------------------------------------------------------------
統計資訊
----------------------------------------------------------
1 recursive calls
0 db block gets
169 consistent gets
0 physical reads
0 redo size
537 bytes sent via SQL*Net to client
519 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
總結:但是真的結論是這樣的麼。其實不然。其實在資料庫中count(*)和count(列)根本就是不等價的,count(*)是針對於全表的,而count(列)是針對於某一列的,如果此列值為空的話,count(列)是不會統計這一行的。所以兩者根本沒有可比性,效能比較首先要考慮寫法等價,這兩個語句根本就不等價。也就失去了去比較的意義!!!
5、測試到底誰更快
SQL> SET SERVEROUTPUT ON
SQL> SET ECHO ON
SQL> DROP TABLE t;
表已刪除。
SQL> DECLARE
2 l_sql VARCHAR2(32767);
3 BEGIN
4 l_sql := 'CREATE TABLE t (';
5 FOR i IN 1..25
6 LOOP
7 l_sql := l_sql || 'n' || i || ' NUMBER,';
8 END LOOP;
9 l_sql := l_sql || 'pad VARCHAR2(1000)) PCTFREE 10';
10 dbms_output.put_line('列印sql語句:'||l_sql);
11 EXECUTE IMMEDIATE l_sql;
12 END;
13 /
列印sql語句:CREATE TABLE t (n1 NUMBER,n2 NUMBER,n3 NUMBER,n4 NUMBER,n5 NUMBER,n6
NUMBER,n7 NUMBER,n8 NUMBER,n9 NUMBER,n10 NUMBER,n11 NUMBER,n12 NUMBER,n13
NUMBER,n14 NUMBER,n15 NUMBER,n16 NUMBER,n17 NUMBER,n18 NUMBER,n19 NUMBER,n20
NUMBER,n21 NUMBER,n22 NUMBER,n23 NUMBER,n24 NUMBER,n25 NUMBER,pad
VARCHAR2(1000)) PCTFREE 10
PL/SQL 過程已成功完成。
向表中填充10000條資料資料:
SQL> DECLARE
2 l_sql VARCHAR2(32767);
3 BEGIN
4 l_sql := 'INSERT INTO t SELECT ';
5 FOR i IN 1..25
6 LOOP
7 l_sql := l_sql || '0,';
8 END LOOP;
9 l_sql := l_sql || 'NULL FROM dual CONNECT BY level <= 10000';
10 dbms_output.put_line('列印sql語句:'||l_sql);
11 EXECUTE IMMEDIATE l_sql;
12 COMMIT;
13 END;
14 /
列印sql語句:INSERT INTO t SELECT
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL FROM dual CONNECT BY
level <= 10000
PL/SQL 過程已成功完成。
--以下動作觀察執行速度,比較發現COUNT(*)最快,COUNT(最大列)最慢
DECLARE
l_dummy PLS_INTEGER;
l_start PLS_INTEGER;
l_stop PLS_INTEGER;
l_sql VARCHAR2(100);
BEGIN
l_start := dbms_utility.get_time;
FOR j IN 1..1000
LOOP
EXECUTE IMMEDIATE 'SELECT count(*) FROM t' INTO l_dummy;
END LOOP;
l_stop := dbms_utility.get_time;
dbms_output.put_line((l_stop-l_start)/100);
FOR i IN 1..25
LOOP
l_sql := 'SELECT count(n' || i || ') FROM t';
l_start := dbms_utility.get_time;
FOR j IN 1..1000
LOOP
EXECUTE IMMEDIATE l_sql INTO l_dummy;
END LOOP;
l_stop := dbms_utility.get_time;
dbms_output.put_line((l_stop-l_start)/100);
END LOOP;
END;
/
--結論:
--原來優化器是這麼搞的:列的偏移量決定效能,列越靠後,訪問的開銷越大。
--由於count(*)的演算法與列偏移量無關,所以count(*)最快。
--開發中將經常訪問的列放到前面,推薦使用count(1)這種寫法。