一、關於count的一些謠言:
1、count(*)比count(val)更慢!項目組必須用count(val),不準用count(*),誰用扣誰錢!
2、count(*)用不到索引,count(val)才能用到。
3、count(*)是統計出全表的記錄,是吞吐量的操作,肯定用不到索引。
4、count(1)比count(*)的速度快。
二、驗證count(*)和count(val)
1、首先創建一個表,使用count(*)和count(val)查詢比較:
<span style="font-size:14px;">----刪除echo表----
SQL> drop table echo purge;
drop table echo purge
*
第 1 行出現錯誤:
ORA-00942: 表或視圖不存在
----創建一張echo的測試表----
SQL> create table echo as select * from dba_objects;
表已創建。
SQL> update echo set object_id = rownum;
已更新72509行。
SQL> commit;
提交完成。
SQL> set timing on
SQL> set linesize 100
SQL> set autotrace on
SQL> select count(*) from echo;
COUNT(*)
----------
已用時間: 00: 00: 00.01
執行計劃
----------------------------------------------------------
Plan hash value: 99109176
-------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 290 (1)| 00:00:04 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| ECHO | 80064 | 290 (1)| 00:00:04 |
-------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
統計信息
----------------------------------------------------------
recursive calls
db block gets
consistent gets
physical reads
redo size
bytes sent via SQL*Net to client
bytes received via SQL*Net from client
SQL*Net roundtrips to/from client
sorts (memory)
sorts (disk)
rows processed
SQL> select count(*) from echo;
COUNT(*)
----------
已用時間: 00: 00: 00.01
執行計劃
----------------------------------------------------------
Plan hash value: 99109176
-------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 290 (1)| 00:00:04 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| ECHO | 80064 | 290 (1)| 00:00:04 |
-------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
統計信息
----------------------------------------------------------
recursive calls
db block gets
consistent gets
physical reads
redo size
bytes sent via SQL*Net to client
bytes received via SQL*Net from client
SQL*Net roundtrips to/from client
sorts (memory)
sorts (disk)
rows processed
SQL> select count(object_id) from echo;
COUNT(OBJECT_ID)
----------------
已用時間: 00: 00: 00.01
執行計劃
----------------------------------------------------------
Plan hash value: 99109176
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 13 | 290 (1)| 00:00:04 |
| 1 | SORT AGGREGATE | | 1 | 13 | | |
| 2 | TABLE ACCESS FULL| ECHO | 80064 | 1016K| 290 (1)| 00:00:04 |
---------------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
統計信息
----------------------------------------------------------
recursive calls
db block gets
consistent gets
physical reads
redo size
bytes sent via SQL*Net to client
bytes received via SQL*Net from client
SQL*Net roundtrips to/from client
sorts (memory)
sorts (disk)
rows processed
SQL> select count(object_id) from echo;
COUNT(OBJECT_ID)
----------------
已用時間: 00: 00: 00.01
執行計劃
----------------------------------------------------------
Plan hash value: 99109176
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 13 | 290 (1)| 00:00:04 |
| 1 | SORT AGGREGATE | | 1 | 13 | | |
| 2 | TABLE ACCESS FULL| ECHO | 80064 | 1016K| 290 (1)| 00:00:04 |
---------------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
統計信息
----------------------------------------------------------
recursive calls
db block gets
consistent gets
physical reads
redo size
bytes sent via SQL*Net to client
bytes received via SQL*Net from client
SQL*Net roundtrips to/from client
sorts (memory)
sorts (disk)
rows processed</span>
從上面的執行計劃可以看出count(*)和count(val)是一樣快的。
2、建立索引做比較
<span style="font-size:14px;">SQL> create index idx_object_id on echo(object_id);
索引已創建。
已用時間: 00: 00: 05.69
SQL> select count(*) from echo;
COUNT(*)
----------
已用時間: 00: 00: 00.05
執行計劃
----------------------------------------------------------
Plan hash value: 99109176
-------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 290 (1)| 00:00:04 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| ECHO | 80064 | 290 (1)| 00:00:04 |
-------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
統計信息
----------------------------------------------------------
recursive calls
db block gets
consistent gets
physical reads
redo size
bytes sent via SQL*Net to client
bytes received via SQL*Net from client
SQL*Net roundtrips to/from client
sorts (memory)
sorts (disk)
rows processed
SQL> select count(object_id) from echo;
COUNT(OBJECT_ID)
----------------
已用時間: 00: 00: 00.08
執行計劃
----------------------------------------------------------
Plan hash value: 1131838604
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 13 | 49 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 13 | | |
| 2 | INDEX FAST FULL SCAN| IDX_OBJECT_ID | 80064 | 1016K| 49 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
統計信息
----------------------------------------------------------
recursive calls
db block gets
consistent gets
physical reads
redo size
bytes sent via SQL*Net to client
bytes received via SQL*Net from client
SQL*Net roundtrips to/from client
sorts (memory)
sorts (disk)
rows processed</span>
哇,原來真的是用count(val)比count(*)要快啊,因爲count(*)不能用到索引,而count(val)可以,真相真是如此嗎?
3、將object_id設置爲非空SQL> alter table echo modify object_id not null;
<span style="font-size:14px;">SQL> alter table echo modify object_id not null;
表已更改。
已用時間: 00: 00: 01.41
SQL> select count(*) from echo;
COUNT(*)
----------
已用時間: 00: 00: 00.00
執行計劃
----------------------------------------------------------
Plan hash value: 1131838604
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 49 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | INDEX FAST FULL SCAN| IDX_OBJECT_ID | 80064 | 49 (0)| 00:00:01 |
-------------------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
統計信息
----------------------------------------------------------
recursive calls
db block gets
consistent gets
physical reads
redo size
bytes sent via SQL*Net to client
bytes received via SQL*Net from client
SQL*Net roundtrips to/from client
sorts (memory)
sorts (disk)
rows processed
SQL> select count(object_id) from echo;
COUNT(OBJECT_ID)
----------------
已用時間: 00: 00: 00.01
執行計劃
----------------------------------------------------------
Plan hash value: 1131838604
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 49 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | INDEX FAST FULL SCAN| IDX_OBJECT_ID | 80064 | 49 (0)| 00:00:01 |
-------------------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
統計信息
----------------------------------------------------------
recursive calls
db block gets
consistent gets
physical reads
redo size
bytes sent via SQL*Net to client
bytes received via SQL*Net from client
SQL*Net roundtrips to/from client
sorts (memory)
sorts (disk)
rows processed</span>
--看來count(val)和count(*)其實一樣快,如果索引列是非空的,count(*)可用到索引,此時一樣快!真相真是如此嗎?
其實兩者根本沒有可比性,性能比較首先考慮寫法等價,這兩個語句根本就不等價。
結論:
其實優化器裏的算法是這麼玩的,列的偏移量決定性能,列越靠後,訪問的開銷越大。由於count(*)的算法與列偏移量無關,所以count(*)最快,count(最後列val)最慢。
3、用實驗驗證上面的結論<span style="font-size:14px;">SQL> set serveroutput on
SQL> set echo on
SQL> drop table t purge;
表已刪除。
----構造出有25個字段的表T----
DECLARE
l_sql VARCHAR2(32767);
BEGIN
l_sql := 'CREATE TABLE t (';
FOR i IN 1..25
LOOP
l_sql := l_sql || 'n' || i || ' NUMBER,';
END LOOP;
l_sql := l_sql || 'pad VARCHAR2(1000)) PCTFREE 10';
EXECUTE IMMEDIATE l_sql;
END;
/
PL/SQL 過程已成功完成。
----將記錄還有這個表T中填充----
DECLARE
l_sql VARCHAR2(32767);
BEGIN
l_sql := 'INSERT INTO t SELECT ';
FOR i IN 1..25
LOOP
l_sql := l_sql || '0,';
END LOOP;
l_sql := l_sql || 'NULL FROM dual CONNECT BY level <= 10000';
EXECUTE IMMEDIATE l_sql;
COMMIT;
END;
/
PL/SQL 過程已成功完成。
execute dbms_stats.gather_table_stats(ownname=>user, tabname=>'t')
SELECT num_rows, blocks FROM user_tables WHERE table_name = 'T';
PL/SQL 過程已成功完成。
SQL>
NUM_ROWS BLOCKS
---------- ----------
80
----以下動作觀察執行速度,比較發現count(*)最快,count(最大列)最慢----
SQL> 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;
/
.18
.33
.39
.38
.42
.4
.45
.49
.48
.46
.48
.48
.55
.51
.56
.57
.61
.62
.75
.67
.68
.7
.73
.78
.77
.81
PL/SQL 過程已成功完成。</span>
三、驗證count(*)和count(1)
沿用TOM大師的解釋: