ITPub博客

首页 > Linux操作系统 > Linux操作系统 > [20120112]count的有关问题.txt

[20120112]count的有关问题.txt

原创 Linux操作系统 作者:lfree 时间:2012-01-12 15:40:04 0 删除 编辑
以前记得在itpub上与别人发生激烈争论,很久以前的事情了.当时争论的焦点是认为count(*),不一定选择主键索引,选择非空的字段,而且索引占用空间最小的索引.
现在写一些例子来说明:

SQL> select * from v$version;
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
PL/SQL Release 11.2.0.1.0 - Production
CORE    11.2.0.1.0      Production
TNS for Linux: Version 11.2.0.1.0 - Production
NLSRTL Version 11.2.0.1.0 - Production

1.建立测试表:
SQL> create table t3 (id1 number,id2 number,name varchar2(20));
Table created.
SQL> create unique index pk_t3 on t3(id1);
Index created.
SQL> alter table t3 add constraint pk_t3 primary key (id1) enable validate;
Table altered.
SQL> insert into t3 select rownum id1,ceil(rownum/1000) id2 ,'test' name from dual connect by level <=10000;
SQL> commit ;
SQL> exec dbms_stats.gather_table_stats(NULL,'T3',Method_Opt=>'FOR ALL COLUMNS SIZE 1');

2.测试1:
SQL>  select /*+ gather_plan_statistics */ count(*) from t3;
SQL> select * from table(dbms_xplan.display_cursor(NULL,NULL,'ALLSTATS LAST PEEKED_BINDS'));
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  fuynj2z4vdjks, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ count(*) from t3
Plan hash value: 1276951659
-----------------------------------------------------------------------------------------
| Id  | Operation             | Name  | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |       |      1 |        |      1 |00:00:00.01 |      25 |
|   1 |  SORT AGGREGATE       |       |      1 |      1 |      1 |00:00:00.01 |      25 |
|   2 |   INDEX FAST FULL SCAN| PK_T3 |      1 |  10000 |  10000 |00:00:00.01 |      25 |
-----------------------------------------------------------------------------------------

--可以发现使用索引PK_T3,使用INDEX FAST FULL SCAN.如果执行如下:

SQL> select /*+ gather_plan_statistics */ count(id2) from t3;
COUNT(ID2)
----------
     10000

SQL> select * from table(dbms_xplan.display_cursor(NULL,NULL,'ALLSTATS LAST PEEKED_BINDS'));
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  f0jazcgr65tmq, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ count(id2) from t3
Plan hash value: 463314188
-------------------------------------------------------------------------------------
| Id  | Operation          | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |      1 |        |      1 |00:00:00.01 |      31 |
|   1 |  SORT AGGREGATE    |      |      1 |      1 |      1 |00:00:00.01 |      31 |
|   2 |   TABLE ACCESS FULL| T3   |      1 |  10000 |  10000 |00:00:00.01 |      31 |
-------------------------------------------------------------------------------------
--可以发现因为id2不为NULL,只能选择TABLE ACCESS FULL.
--修改id2为非空:

SQL> alter table t3 modify(id2  not null);

SQL> select /*+ gather_plan_statistics */ count(id2) from t3;
COUNT(ID2)
----------
     10000

SQL> select * from table(dbms_xplan.display_cursor(NULL,NULL,'ALLSTATS LAST PEEKED_BINDS'));
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  f0jazcgr65tmq, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ count(id2) from t3
Plan hash value: 1276951659
-----------------------------------------------------------------------------------------
| Id  | Operation             | Name  | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |       |      1 |        |      1 |00:00:00.01 |      25 |
|   1 |  SORT AGGREGATE       |       |      1 |      1 |      1 |00:00:00.01 |      25 |
|   2 |   INDEX FAST FULL SCAN| PK_T3 |      1 |  10000 |  10000 |00:00:00.01 |      25 |
-----------------------------------------------------------------------------------------
--可以发现即使count是id2,由于id2是not null,oracle可以选择PK_T3(id1的索引)的INDEX FAST FULL SCAN.

3.测试2:
--修改name为非NULL,建立name索引,选择索引压缩方式.
SQL> alter table t3 modify(name  not null);
SQL> CREATE INDEX i_t3_name ON T3(NAME) COMPRESS 1;
--重新分析表.
SQL> exec dbms_stats.gather_table_stats(NULL,'T3',Method_Opt=>'FOR ALL COLUMNS SIZE 1');
SQL> select index_name,compression,blevel,leaf_blocks from user_indexes where table_name='T3';
INDEX_NAME                     COMPRESS     BLEVEL LEAF_BLOCKS
------------------------------ -------- ---------- -----------
I_T3_NAME                      ENABLED           1          16
PK_T3                          DISABLED          1          18

--可以发现I_T3_name由于重复值很多,索引占用空间相对PK_T3小一点.

SQL> select /*+ gather_plan_statistics */ count(id2) from t3;
COUNT(ID2)
----------
     10000

SQL> select * from table(dbms_xplan.display_cursor(NULL,NULL,'ALLSTATS LAST PEEKED_BINDS'));
PLAN_TABLE_OUTPUT
--------------------------------------
SQL_ID  f0jazcgr65tmq, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ count(id2) from t3

Plan hash value: 1645538918

---------------------------------------------------------------------------------------------
| Id  | Operation             | Name      | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |           |      1 |        |      1 |00:00:00.01 |      22 |
|   1 |  SORT AGGREGATE       |           |      1 |      1 |      1 |00:00:00.01 |      22 |
|   2 |   INDEX FAST FULL SCAN| I_T3_NAME |      1 |  10000 |  10000 |00:00:00.01 |      22 |
---------------------------------------------------------------------------------------------
--可以发现执行选择了i_t3_name索引,因为它占用的空间要比PK_T3.

4.测试3:
--修改id2为NULL,建立位图索引.看看情况如何呢?

SQL> alter table t3 modify(id2 null);
SQL> create bitmap index i_t3_id2 on t3(id2);
SQL> exec dbms_stats.gather_table_stats(NULL,'T3',Method_Opt=>'FOR ALL COLUMNS SIZE 1');

SQL> select index_name,compression,blevel,leaf_blocks from user_indexes where table_name='T3';
INDEX_NAME                     COMPRESS     BLEVEL LEAF_BLOCKS
------------------------------ -------- ---------- -----------
I_T3_ID2                       DISABLED          0           1
I_T3_NAME                      ENABLED           1          16
PK_T3                          DISABLED          1          18

--这时可以发现位图索引i_t3_id2占用空间最小.

SQL> select /*+ gather_plan_statistics */ count(id2) from t3;
COUNT(ID2)
----------
     10000

SQL> select * from table(dbms_xplan.display_cursor(NULL,NULL,'ALLSTATS LAST PEEKED_BINDS'));
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  f0jazcgr65tmq, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ count(id2) from t3
Plan hash value: 1323983206
----------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |          |      1 |        |      1 |00:00:00.01 |       4 |
|   1 |  SORT AGGREGATE               |          |      1 |      1 |      1 |00:00:00.01 |       4 |
|   2 |   BITMAP CONVERSION TO ROWIDS |          |      1 |  10000 |  10000 |00:00:00.01 |       4 |
|   3 |    BITMAP INDEX FAST FULL SCAN| I_T3_ID2 |      1 |        |     10 |00:00:00.01 |       4 |
----------------------------------------------------------------------------------------------------
--可以发现位图索引的特殊方式,可以记录NULL值,执行计划可以使用位图索引I_T3_ID2.
--对比如下看看如何?

SQL> select /*+ gather_plan_statistics */ count(*) from t3;
  COUNT(*)
----------
     10000

SQL> select * from table(dbms_xplan.display_cursor(NULL,NULL,'ALLSTATS LAST PEEKED_BINDS'));
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  fuynj2z4vdjks, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ count(*) from t3
Plan hash value: 2432646254
----------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |          |      1 |        |      1 |00:00:00.01 |       4 |
|   1 |  SORT AGGREGATE               |          |      1 |      1 |      1 |00:00:00.01 |       4 |
|   2 |   BITMAP CONVERSION COUNT     |          |      1 |  10000 |     10 |00:00:00.01 |       4 |
|   3 |    BITMAP INDEX FAST FULL SCAN| I_T3_ID2 |      1 |        |     10 |00:00:00.01 |       4 |
----------------------------------------------------------------------------------------------------

--咋一看,好像一样的,对比发现Plan hash value不同,仔细看,就能看出不同:
前者第2步执行选择的是BITMAP CONVERSION TO ROWIDS,而后者执行选择的是BITMAP CONVERSION COUNT.
因为前面count(id),是计数id2为非空的数量,也就是扫描位图索引时不包括NULL.
而后面count(*),是计数全部包括空值.说明细节很重要!

5.测试4:
--如果修改id为非NULL
SQL> alter table t3 modify(id2 not null);
SQL> select /*+ gather_plan_statistics */ count(id2) from t3;
COUNT(ID2)
----------
     10000

SQL> select * from table(dbms_xplan.display_cursor(NULL,NULL,'ALLSTATS LAST PEEKED_BINDS'));
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  f0jazcgr65tmq, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ count(id2) from t3
Plan hash value: 2432646254
----------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |          |      1 |        |      1 |00:00:00.01 |       4 |
|   1 |  SORT AGGREGATE               |          |      1 |      1 |      1 |00:00:00.01 |       4 |
|   2 |   BITMAP CONVERSION COUNT     |          |      1 |  10000 |     10 |00:00:00.01 |       4 |
|   3 |    BITMAP INDEX FAST FULL SCAN| I_T3_ID2 |      1 |        |     10 |00:00:00.01 |       4 |
----------------------------------------------------------------------------------------------------

--可以发现这样就与count(*)的执行计划相同!


来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/267265/viewspace-714828/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
熟悉oracle相关技术,擅长sql优化,rman备份与恢复,熟悉linux shell编程。

注册时间:2008-01-03

  • 博文量
    2351
  • 访问量
    6093228