1. 程式人生 > >二十六、COUNT(*)與COUNT(列)到底誰更快?

二十六、COUNT(*)與COUNT(列)到底誰更快?

        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)這種寫法。