【DB笔试面试622】在Oracle中,说说COUNT(*)计算行数有哪些优化手段?_Oracle










题目部分

在Oracle中,说说COUNT(*)计算行数有哪些优化手段?





     













答案部分




手段

命令

执行计划

主要原理

详细说明

性能情况

全表扫描


TABLE ACCESS FULL

全表扫描

OLTP中,通常是最慢的方式。

逻辑读为1139

增加普通索引

CREATE  INDEX  IDX_OBJECT_NAME ON T(OBJECT_NAME);

INDEX FAST FULL SCAN

从全表扫描转成全索引扫描。

因为索引一般比表小的多,所以全表扫描转成全索引扫描,性能能大幅度提升。

逻辑读为400

常数索引

CREATE  INDEX  IDX_OBJECT_NAME ON T(0);

INDEX FAST FULL SCAN

从全表扫描转成全索引扫描。

常数索引比普通索引更小。

逻辑读为151

常数压缩索引

CREATE  INDEX  IDX_OBJECT_NAME ON T(0) COMPRESS;

INDEX FAST FULL SCAN

从全表扫描转成全索引扫描。

常数压缩索引比常数索引更小。

逻辑读为129

位图索引

CREATE BITMAP INDEX IDX_OBJECT_NAME ON T(OBJECT_NAME);

BITMAP INDEX FAST FULL SCAN

从BTREE索引扫描转成位图索引扫描。

位图索引的大小比BTREE索引要小的多,所以位图索引扫描快。

逻辑读为5

物化视图

CREATE  MATERIALIZED VIEW  MV_COUNT_T

   BUILD IMMEDIATE

   REFRESH ON COMMIT

   ENABLE QUERY REWRITE

AS  SELECT COUNT(*) FROM T;

MAT_VIEW REWRITE ACCESS FULL

空间换时间。

要注意,如果数据要求比较实时,就不适用。

逻辑读为3

缓存结果

SELECT /*+ RESULT_CACHE */ COUNT(*) FROM T;

RESULT CACHE

直接把查询结果拿来用。

要注意,如果数据频繁更新,就不适用。

逻辑读为0

业务理解

SELECT COUNT(*) FROM T WHERE ROWNUM=1;


如果COUNT(*)只是为了判断条数,就加上ROWNUM=1来判断是否为1。

业务需求转换,获取条数有的时候,只是为了看看表是否为空,这时候是否是1条和是否大于0其实是一样的。

不言而喻

分析需求

据说,这个COUNT(*)统计条数语句,是多余的!直接砍了这条语句,这里没有SQL!

无敌!

 

位图索引可以按很高密度存储数据,因此往往比B树索引小很多,前提是在基数比较小(列重复度比较高)的情况下。位图索引是保存空值的,因此可以在COUNT中利用。位图索引不太适合OLTP类型数据库。物化视图是应用在数据要求不怎么及时的场景下。若表频繁更新,则不适合缓存结果集。

优化没有止境,对数据库了解越多,能想到的方法就越多。




 1--无索引
 2DROP TABLE T_20170704_COUNT_LHR_01 PURGE;
 3CREATE TABLE T_20170704_COUNT_LHR_01 AS  SELECT * FROM DBA_OBJECTS;
 4ALTER TABLE T_20170704_COUNT_LHR_01 MODIFY OBJECT_NAME NOT NULL;
 5SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
 6SET AUTOTRACE TRACEONLY
 7SET LINESIZE 1000
 8SET TIMING ON
 9SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
10
11
12--普通索引
13DROP TABLE T_20170704_COUNT_LHR_01 PURGE;
14CREATE TABLE T_20170704_COUNT_LHR_01 AS  SELECT * FROM DBA_OBJECTS;
15ALTER TABLE T_20170704_COUNT_LHR_01 MODIFY OBJECT_NAME NOT NULL;
16CREATE  INDEX IDX_OBJECT_NAME ON T_20170704_COUNT_LHR_01(OBJECT_NAME);
17SET AUTOTRACE TRACEONLY
18SET TIMING ON
19SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
20
21
22--唯一索引
23DROP TABLE T_20170704_COUNT_LHR_01 PURGE;
24CREATE TABLE T_20170704_COUNT_LHR_01 AS  SELECT * FROM DBA_OBJECTS;
25ALTER TABLE T_20170704_COUNT_LHR_01 MODIFY OBJECT_NAME NOT NULL;
26UPDATE T_20170704_COUNT_LHR_01 T SET T.OBJECT_NAME=T.OBJECT_NAME||ROWNUM;
27CREATE unique INDEX IDX_OBJECT_NAME ON T_20170704_COUNT_LHR_01(OBJECT_NAME);
28SET AUTOTRACE TRACEONLY
29SET TIMING ON
30SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
31
32
33--常数索引
34DROP TABLE T_20170704_COUNT_LHR_01 PURGE;
35CREATE TABLE T_20170704_COUNT_LHR_01 AS  SELECT * FROM DBA_OBJECTS;
36ALTER TABLE T_20170704_COUNT_LHR_01 MODIFY OBJECT_NAME NOT NULL;
37CREATE INDEX IDX_OBJECT_NAME ON T_20170704_COUNT_LHR_01(0);
38SET AUTOTRACE TRACEONLY
39SET TIMING ON
40SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
41
42
43--常数压缩索引
44DROP TABLE T_20170704_COUNT_LHR_01 PURGE;
45CREATE TABLE T_20170704_COUNT_LHR_01 AS  SELECT * FROM DBA_OBJECTS;
46ALTER TABLE T_20170704_COUNT_LHR_01 MODIFY OBJECT_NAME NOT NULL;
47CREATE INDEX IDX_OBJECT_NAME ON T_20170704_COUNT_LHR_01(0) COMPRESS;
48SET AUTOTRACE TRACEONLY
49SET TIMING ON
50SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
51
52
53
54
55--位图索引
56DROP TABLE T_20170704_COUNT_LHR_01 PURGE;
57CREATE TABLE T_20170704_COUNT_LHR_01 AS  SELECT * FROM DBA_OBJECTS;
58UPDATE T_20170704_COUNT_LHR_01  SET OBJECT_NAME='lhr';
59UPDATE T_20170704_COUNT_LHR_01 SET OBJECT_NAME='ldd' WHERE ROWNUM<=20000;
60CREATE BITMAP INDEX IDX_OBJECT_NAME ON T_20170704_COUNT_LHR_01(OBJECT_NAME);
61SET AUTOTRACE TRACEONLY
62SET TIMING ON
63SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
64ALTER INDEX IDX_OBJECT_NAME PARALLEL 8;
65SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
66
67
68-- 物化视图
69DROP MATERIALIZED VIEW MV_COUNT_T_LHR;
70DROP TABLE T_20170704_COUNT_LHR_01 PURGE;
71CREATE TABLE T_20170704_COUNT_LHR_01 AS  SELECT * FROM DBA_OBJECTS;
72UPDATE T_20170704_COUNT_LHR_01  SET OBJECT_NAME='lhr';
73UPDATE T_20170704_COUNT_LHR_01 SET OBJECT_NAME='ldd' WHERE ROWNUM<=20000;
74CREATE  MATERIALIZED VIEW  MV_COUNT_T_LHR
75BUILD IMMEDIATE
76REFRESH ON COMMIT
77ENABLE QUERY REWRITE
78AS
79SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
80SET AUTOTRACE TRACEONLY
81SET LINESIZE 1000
82SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
83
84
85--缓存结果集
86DROP TABLE T_20170704_COUNT_LHR_01 PURGE;
87CREATE TABLE T_20170704_COUNT_LHR_01 AS  SELECT * FROM DBA_OBJECTS;
88SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
89SET LINESIZE 1000
90SET AUTOTRACE TRACEONLY
91SELECT /*+ RESULT_CACHE */ COUNT(*) FROM T_20170704_COUNT_LHR_01;
92
93
94--业务分析
95SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01 WHERE ROWNUM=1;


一、 普通表(无索引)



 1DROP TABLE T_20170704_COUNT_LHR_01 PURGE;
 2CREATE TABLE T_20170704_COUNT_LHR_01 AS  SELECT * FROM DBA_OBJECTS;
 3ALTER TABLE T_20170704_COUNT_LHR_01 MODIFY OBJECT_NAME NOT NULL;
 4
 5LHR@orclasm > SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
 6
 7  COUNT(*)
 8----------
 9     79300
10
11LHR@orclasm > SET AUTOTRACE TRACEONLY
12LHR@orclasm > SET LINESIZE 1000
13LHR@orclasm > SET TIMING ON
14LHR@orclasm > LHR@orclasm > LHR@orclasm > SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
15
16Elapsed: 00:00:00.01
17
18Execution Plan
19----------------------------------------------------------
20Plan hash value: 1395805058
21
22--------------------------------------------------------------------------------------
23| Id  | Operation          | Name                    | Rows  | Cost (%CPU)| Time     |
24--------------------------------------------------------------------------------------
25|   0 | SELECT STATEMENT   |                         |     1 |   317   (1)| 00:00:04 |
26|   1 |  SORT AGGREGATE    |                         |     1 |            |          |
27|   2 |   TABLE ACCESS FULL| T_20170704_COUNT_LHR_01 | 91429 |   317   (1)| 00:00:04 |
28--------------------------------------------------------------------------------------
29
30Note
31-----
32   - dynamic sampling used for this statement (level=2)
33
34
35Statistics
36----------------------------------------------------------
37          0  recursive calls
38          0  db block gets
39       1139  consistent gets
40          0  physical reads
41          0  redo size
42        527  bytes sent via SQL*Net to client
43        520  bytes received via SQL*Net from client
44          2  SQL*Net roundtrips to/from client
45          0  sorts (memory)
46          0  sorts (disk)
47          1  rows processed

二、 普通索引


 1DROP TABLE T_20170704_COUNT_LHR_01 PURGE;
 2CREATE TABLE T_20170704_COUNT_LHR_01 AS  SELECT * FROM DBA_OBJECTS;
 3ALTER TABLE T_20170704_COUNT_LHR_01 MODIFY OBJECT_NAME NOT NULL;
 4CREATE  INDEX IDX_OBJECT_NAME ON T_20170704_COUNT_LHR_01(OBJECT_NAME);
 5SET AUTOTRACE TRACEONLY
 6SET TIMING ON
 7
 8LHR@orclasm > SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
 9
10Elapsed: 00:00:00.20
11
12Execution Plan
13----------------------------------------------------------
14Plan hash value: 1178070731
15
16---------------------------------------------------------------------------------
17| Id  | Operation             | Name            | Rows  | Cost (%CPU)| Time     |
18---------------------------------------------------------------------------------
19|   0 | SELECT STATEMENT      |                 |     1 |   114   (1)| 00:00:02 |
20|   1 |  SORT AGGREGATE       |                 |     1 |            |          |
21|   2 |   INDEX FAST FULL SCAN| IDX_OBJECT_NAME | 91429 |   114   (1)| 00:00:02 |
22---------------------------------------------------------------------------------
23
24Note
25-----
26   - dynamic sampling used for this statement (level=2)
27
28
29Statistics
30----------------------------------------------------------
31          0  recursive calls
32          0  db block gets
33        400  consistent gets
34          0  physical reads
35          0  redo size
36        527  bytes sent via SQL*Net to client
37        520  bytes received via SQL*Net from client
38          2  SQL*Net roundtrips to/from client
39          0  sorts (memory)
40          0  sorts (disk)
41          1  rows processed



 1--1、主键索引(唯一索引)
 2DROP TABLE T_20170704_COUNT_LHR_01 PURGE;
 3CREATE TABLE T_20170704_COUNT_LHR_01 AS  SELECT * FROM DBA_OBJECTS;
 4ALTER TABLE T_20170704_COUNT_LHR_01 MODIFY OBJECT_NAME NOT NULL;
 5CREATE UNIQUE INDEX IDX_OBJECT_NAME ON T_20170704_COUNT_LHR_01(OBJECT_NAME);
 6SET AUTOTRACE TRACEONLY
 7SET TIMING ON
 8SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
 9
10--2、常数索引
11DROP TABLE T_20170704_COUNT_LHR_01 PURGE;
12CREATE TABLE T_20170704_COUNT_LHR_01 AS  SELECT * FROM DBA_OBJECTS;
13ALTER TABLE T_20170704_COUNT_LHR_01 MODIFY OBJECT_NAME NOT NULL;
14CREATE INDEX IDX_OBJECT_NAME ON T_20170704_COUNT_LHR_01(0);
15SET AUTOTRACE TRACEONLY
16SET TIMING ON
17SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
18LHR@orclasm > SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
19
20Elapsed: 00:00:00.01
21
22Execution Plan
23----------------------------------------------------------
24Plan hash value: 1178070731
25
26---------------------------------------------------------------------------------
27| Id  | Operation             | Name            | Rows  | Cost (%CPU)| Time     |
28---------------------------------------------------------------------------------
29|   0 | SELECT STATEMENT      |                 |     1 |    45   (3)| 00:00:01 |
30|   1 |  SORT AGGREGATE       |                 |     1 |            |          |
31|   2 |   INDEX FAST FULL SCAN| IDX_OBJECT_NAME | 86597 |    45   (3)| 00:00:01 |
32---------------------------------------------------------------------------------
33
34Note
35-----
36   - dynamic sampling used for this statement (level=2)
37
38
39Statistics
40----------------------------------------------------------
41          0  recursive calls
42          0  db block gets
43        151  consistent gets
44          0  physical reads
45          0  redo size
46        528  bytes sent via SQL*Net to client
47        520  bytes received via SQL*Net from client
48          2  SQL*Net roundtrips to/from client
49          0  sorts (memory)
50          0  sorts (disk)
51          1  rows processed
52
53
54--3、常数压缩索引
55DROP TABLE T_20170704_COUNT_LHR_01 PURGE;
56CREATE TABLE T_20170704_COUNT_LHR_01 AS  SELECT * FROM DBA_OBJECTS;
57ALTER TABLE T_20170704_COUNT_LHR_01 MODIFY OBJECT_NAME NOT NULL;
58CREATE INDEX IDX_OBJECT_NAME ON T_20170704_COUNT_LHR_01(0) COMPRESS;
59SET AUTOTRACE TRACEONLY
60SET TIMING ON
61SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
62LHR@orclasm > LHR@orclasm > LHR@orclasm > SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
63
64Elapsed: 00:00:00.00
65
66Execution Plan
67----------------------------------------------------------
68Plan hash value: 1178070731
69
70---------------------------------------------------------------------------------
71| Id  | Operation             | Name            | Rows  | Cost (%CPU)| Time     |
72---------------------------------------------------------------------------------
73|   0 | SELECT STATEMENT      |                 |     1 |    38   (0)| 00:00:01 |
74|   1 |  SORT AGGREGATE       |                 |     1 |            |          |
75|   2 |   INDEX FAST FULL SCAN| IDX_OBJECT_NAME | 86597 |    38   (0)| 00:00:01 |
76---------------------------------------------------------------------------------
77
78Note
79-----
80   - dynamic sampling used for this statement (level=2)
81
82
83Statistics
84----------------------------------------------------------
85          0  recursive calls
86          0  db block gets
87        129  consistent gets
88          0  physical reads
89          0  redo size
90        528  bytes sent via SQL*Net to client
91        520  bytes received via SQL*Net from client
92          2  SQL*Net roundtrips to/from client
93          0  sorts (memory)
94          0  sorts (disk)
95          1  rows processed

三、 位图索引

试验如下:


 1DROP TABLE T_20170704_COUNT_LHR_01 PURGE;
 2CREATE TABLE T_20170704_COUNT_LHR_01 AS  SELECT * FROM DBA_OBJECTS;
 3UPDATE T_20170704_COUNT_LHR_01  SET OBJECT_NAME='lhr';
 4UPDATE T_20170704_COUNT_LHR_01 SET OBJECT_NAME='ldd' WHERE ROWNUM<=20000;
 5CREATE BITMAP INDEX IDX_OBJECT_NAME ON T_20170704_COUNT_LHR_01(OBJECT_NAME);
 6SET AUTOTRACE TRACEONLY
 7SET TIMING ON
 8SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
 9LHR@orclasm > SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
10
11Elapsed: 00:00:00.00
12
13Execution Plan
14----------------------------------------------------------
15Plan hash value: 1696023018
16
17-----------------------------------------------------------------------------------------
18| Id  | Operation                     | Name            | Rows  | Cost (%CPU)| Time     |
19-----------------------------------------------------------------------------------------
20|   0 | SELECT STATEMENT              |                 |     1 |     5   (0)| 00:00:01 |
21|   1 |  SORT AGGREGATE               |                 |     1 |            |          |
22|   2 |   BITMAP CONVERSION COUNT     |                 | 91429 |     5   (0)| 00:00:01 |
23|   3 |    BITMAP INDEX FAST FULL SCAN| IDX_OBJECT_NAME |       |            |          |
24-----------------------------------------------------------------------------------------
25
26Note
27-----
28   - dynamic sampling used for this statement (level=2)
29
30
31Statistics
32----------------------------------------------------------
33          0  recursive calls
34          0  db block gets
35          5  consistent gets
36          0  physical reads
37          0  redo size
38        527  bytes sent via SQL*Net to client
39        520  bytes received via SQL*Net from client
40          2  SQL*Net roundtrips to/from client
41          0  sorts (memory)
42          0  sorts (disk)
43          1  rows processed

1、 位图索引+并行


1ALTER INDEX IDX_OBJECT_NAME PARALLEL 8;
2
3SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;

并行技术可以加快执行速度,但一致性读有所增加,但并行还是能加快整体运行速度。

四、 物化视图

这主要是应用在数据库更新不是非常频繁场景,用的是空间换时间。


 1DROP MATERIALIZED VIEW MV_COUNT_T_LHR;
 2DROP TABLE T_20170704_COUNT_LHR_01 PURGE;
 3CREATE TABLE T_20170704_COUNT_LHR_01 AS  SELECT * FROM DBA_OBJECTS;
 4UPDATE T_20170704_COUNT_LHR_01  SET OBJECT_NAME='lhr';
 5UPDATE T_20170704_COUNT_LHR_01 SET OBJECT_NAME='ldd' WHERE ROWNUM<=20000;
 6CREATE  MATERIALIZED VIEW  MV_COUNT_T_LHR
 7BUILD IMMEDIATE
 8REFRESH ON COMMIT
 9ENABLE QUERY REWRITE
10AS
11SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
12SET AUTOTRACE TRACEONLY
13SET LINESIZE 1000
14SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
15LHR@orclasm > SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
16
17Elapsed: 00:00:00.08
18
19Execution Plan
20----------------------------------------------------------
21Plan hash value: 571421573
22
23-----------------------------------------------------------------------------------------------
24| Id  | Operation                    | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
25-----------------------------------------------------------------------------------------------
26|   0 | SELECT STATEMENT             |                |     1 |    13 |     3   (0)| 00:00:01 |
27|   1 |  MAT_VIEW REWRITE ACCESS FULL| MV_COUNT_T_LHR |     1 |    13 |     3   (0)| 00:00:01 |
28-----------------------------------------------------------------------------------------------
29
30Note
31-----
32   - dynamic sampling used for this statement (level=2)
33
34
35Statistics
36----------------------------------------------------------
37          0  recursive calls
38          0  db block gets
39          3  consistent gets
40          0  physical reads
41          0  redo size
42        531  bytes sent via SQL*Net to client
43        520  bytes received via SQL*Net from client
44          2  SQL*Net roundtrips to/from client
45          0  sorts (memory)
46          0  sorts (disk)
47          1  rows processed

五、 缓存结果

在Oracle 11g中提供了结果集缓存特性。该缓存是在共享内存中存储全部的结果集。如果一个查询SQL被执行,且它对应的结果集在缓存中,那么,该SQL的几乎全部开销都可以避免。


 1DROP TABLE T_20170704_COUNT_LHR_01 PURGE;
 2CREATE TABLE T_20170704_COUNT_LHR_01 AS  SELECT * FROM DBA_OBJECTS;
 3SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
 4SET LINESIZE 1000
 5SET AUTOTRACE TRACEONLY
 6SELECT /*+ RESULT_CACHE */ COUNT(*) FROM T_20170704_COUNT_LHR_01;
 7LHR@orclasm > LHR@orclasm > SELECT /*+ RESULT_CACHE */ COUNT(*) FROM T_20170704_COUNT_LHR_01;
 8
 9Elapsed: 00:00:00.00
10
11Execution Plan
12----------------------------------------------------------
13Plan hash value: 1395805058
14
15------------------------------------------------------------------------------------------
16| Id  | Operation           | Name                       | Rows  | Cost (%CPU)| Time     |
17------------------------------------------------------------------------------------------
18|   0 | SELECT STATEMENT    |                            |     1 |   317   (1)| 00:00:04 |
19|   1 |  RESULT CACHE       | 6pp2f468gdjnj9v3s3mfwffd7t |       |            |          |
20|   2 |   SORT AGGREGATE    |                            |     1 |            |          |
21|   3 |    TABLE ACCESS FULL| T_20170704_COUNT_LHR_01    | 86597 |   317   (1)| 00:00:04 |
22------------------------------------------------------------------------------------------
23
24Result Cache Information (identified by operation id):
25------------------------------------------------------
26
27   1 - column-count=1; dependencies=(LHR.T_20170704_COUNT_LHR_01); attributes=(single-row); name="SELECT /*+ RESULT_CACHE */ COUNT(*) FROM T_20170704_COUNT_LHR_01"
28
29Note
30-----
31   - dynamic sampling used for this statement (level=2)
32
33
34Statistics
35----------------------------------------------------------
36          0  recursive calls
37          0  db block gets
38          0  consistent gets
39          0  physical reads
40          0  redo size
41        528  bytes sent via SQL*Net to client
42        520  bytes received via SQL*Net from client
43          2  SQL*Net roundtrips to/from client
44          0  sorts (memory)
45          0  sorts (disk)
46          1  rows processed
47

六、 根据业务规则判断

若统计行数只是为了判断表中是否有记录,则可以使用ROWNUM=1,所以改写后的SQL变为:


1SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01 WHERE ROWNUM=1;

该SQL无论表中数据多大,性能都不会太差。

 

八、 分析需求

仔细分析需求后,可能会发现,统计行数这条SQL根本就是多余的,那么这条SQL语句就可以直接砍掉了。