当前位置:  数据库>oracle

Oracle取得表中总记录数最快的方法

    来源: 互联网  发布时间:2017-06-02

    本文导语: 查询表中的记录总数的语法就是SELECT COUNT(*) FROM TABLE_NAME。这可能是最经常使用的一类SQL语句。 本文讨论怎样才能最快的得到这个记录数。本文纯粹主要是理论上的讨论,文章中很多内容(如常数索引)对实际的指导意义不大。...

查询表中的记录总数的语法就是SELECT COUNT(*) FROM TABLE_NAME。这可能是最经常使用的一类SQL语句。
 
本文讨论怎样才能最快的得到这个记录数。本文纯粹主要是理论上的讨论,文章中很多内容(如常数索引)对实际的指导意义不大。
 
在具体描述之前,强调几个前提:
 
首先表中的记录数不能太少,否则讨论的意义就不大了,在我下面的例子中记录数是3万左右,其实这个数量级还是比较小,不过已经能够看出一些效果了。
 
根据执行时间的长短进行判断偶然性比较大,本文以没种方法逻辑读的多少来进行判断。由于包括查询重写(需要的相对较多的执行计划的分析)和索引压缩(属于CPU密集型,消耗CPU资源较多),仅仅用逻辑读来衡量各种方法的优劣肯定不会很准确,但是考虑到表中的数据量比较大,而且我们以SQL的第二次执行结果为准,所以,其他方面的影响还是可以忽略的。
 
另外一个前提就是结果的准确性,查询USER_TABLES的NUM_ROWS列等类似的方法不在本文讨论范畴之内。
 
最后,由于Oracle的缓存和共享池的机制,SQL语句逻辑读一般从第二次执行才稳定下来,出于篇幅的考虑,下面所有的SELECT COUNT(*) FROM T的结果都是该SQL语句第二次执行的结果。
 
如果存在一个查询语句为SELECT COUNT(*)的物化视图,则最快的方式一定是扫描这张物化视图。
 
SQL> CREATE TABLE T (ID NUMBER NOT NULL, NAME VARCHAR2(30), TYPE VARCHAR2(18));
 
表已创建。
 
SQL> INSERT INTO T SELECT ROWNUM, OBJECT_NAME, OBJECT_TYPE FROM DBA_OBJECTS;
 
已创建30931行。
 
SQL> COMMIT;
 
提交完成。
 
SQL> CREATE MATERIALIZED VIEW LOG ON T WITH ROWID INCLUDING NEW VALUES;
 
实体化视图日志已创建。
 
SQL> CREATE MATERIALIZED VIEW MV_T REFRESH FAST ON COMMIT ENABLE QUERY REWRITE AS
 2 SELECT COUNT(*) FROM T;
 
实体化视图已创建。
 
SQL> ALTER SESSION SET QUERY_REWRITE_ENABLED = TRUE;
 
会话已更改。
 
SQL> EXEC DBMS_STATS.GATHER_TABLE_STATS(USER, 'T')
 
PL/SQL 过程已成功完成。
 
SQL> SET AUTOT ON
 SQL> SELECT COUNT(*) FROM T;
 
COUNT(*)
 ----------
 30931
 
Execution Plan
 ----------------------------------------------------------
 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=82 Bytes=1066)
 1 0 TABLE ACCESS (FULL) OF 'MV_T' (Cost=2 Card=82 Bytes=1066)
 
Statistics
 ----------------------------------------------------------
 0 recursive calls
 0 db block gets
 3 consistent gets
 0 physical reads
 0 redo size
 378 bytes sent via SQL*Net to client
 503 bytes received via SQL*Net from client
 2 SQL*Net roundtrips to/from client
 0 sorts (memory)
 0 sorts (disk)
 1 rows processed
 
根据上面的查询可以看出,扫描物化视图,只需3个逻辑读就可以了。但是,物化视图对系统的限制比较多。首先要创建物化视图日志,还要在SYSTEM或SESSION级设置参数,必须使用CBO等很多的条件,限制了物化视图的使用,而且最重要的是,一般情况下不会存在一个单纯查询全表记录数的物化视图,而一般建立的物化视图是为了加快一些更加复杂的表连接或聚集的查询的。因此,即使存在物化视图,也不会直接得到结果,一般是对物化视图上的结果进行再次计算。
 
如果不考虑物化视图,那么得到记录总数的最快的方法一定是BITMAP索引扫描。BITMAP索引的机制使得BITMAP索引回答COUNT(*)之类的查询具有最快的响应速度和最小的逻辑读。至于BITMAP索引的机制,这里就不重复描述了,还是看看BITMAP索引的表现吧:
 
SQL> DROP MATERIALIZED VIEW MV_T;
 
实体化视图已删除。
 
SQL> DROP MATERIALIZED VIEW LOG ON T;
 
实体化视图日志已删除。
 
SQL> CREATE BITMAP INDEX IND_B_T_TYPE ON T (TYPE);
 
索引已创建。
 
SQL> EXEC DBMS_STATS.GATHER_INDEX_STATS(USER, 'IND_B_T_TYPE')
 
PL/SQL 过程已成功完成。
 
SQL> SELECT COUNT(*) FROM T;
 
COUNT(*)
 ----------
 30931
 
Execution Plan
 ----------------------------------------------------------
 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1)
 1 0 SORT (AGGREGATE)
 2 1 BITMAP CONVERSION (COUNT)
 3 2 BITMAP INDEX (FAST FULL SCAN) OF 'IND_B_T_TYPE'
 
Statistics
 ----------------------------------------------------------
 0 recursive calls
 0 db block gets
 5 consistent gets
 0 physical reads
 0 redo size
 378 bytes sent via SQL*Net to client
 503 bytes received via SQL*Net from client
 2 SQL*Net roundtrips to/from client
 0 sorts (memory)
 0 sorts (disk)
 1 rows processed
 
可以看到,BITMAP索引的表现十分出色,只需5个逻辑读就可以得到结果。可惜的是,BITMAP索引比较适合在数据仓库中使用,而对于OLTP环境,BITMAP索引的锁粒度将给整个系统带来严重的灾难。因此,对于OLTP系统,BITMAP索引也是不合适的。
 
不考虑BITMAP索引,那么速度最快的应该是普通索引的快速全扫了,比如主键列。
 
SQL> DROP INDEX IND_B_T_TYPE;
 
索引已丢弃。
 
SQL> ALTER TABLE T ADD CONSTRAINT PK_T PRIMARY KEY (ID);
 
表已更改。
 
SQL> SELECT COUNT(*) FROM T;
 
COUNT(*)
 ----------
 30931
 
Execution Plan
 ----------------------------------------------------------
 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=4 Card=1)
 1 0 SORT (AGGREGATE)
 2 1 INDEX (FAST FULL SCAN) OF 'PK_T' (UNIQUE) (Cost=4 Card=30931)
 
Statistics
 ----------------------------------------------------------
 0 recursive calls
 0 db block gets
 69 consistent gets
 0 physical reads
 0 redo size
 378 bytes sent via SQL*Net to client
 503 bytes received via SQL*Net from client
 2 SQL*Net roundtrips to/from client
 0 sorts (memory)
 0 sorts (disk)
 1 rows processed
 
主键的快速全扫只需69个逻辑读。但是由于主键这里用的是ROWNUM,也就是说是主键的值是从1到30931,Oracle存储这些NUMBER类型则需要2到4位不等。如果建立一个常数索引,则在存储空间上要节省一些。而在执行索引快速全扫时,就能减少一些逻辑读。
 
SQL> CREATE INDEX IND_T_CON ON T(1);
 
索引已创建。
 
SQL> SELECT COUNT(*) FROM T;
 
COUNT(*)
 ----------
 30931
 
Execution Plan
 ----------------------------------------------------------
 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=4 Card=1)
 1 0 SORT (AGGREGATE)
 2 1 INDEX (FAST FULL SCAN) OF 'IND_T_CON' (NON-UNIQUE) (Cost=4 Card=30931)
 
Statistics
 ----------------------------------------------------------
 0 recursive calls
 0 db block gets
 66 consistent gets
 0 physical reads
 0 redo size
 378 bytes sent via SQL*Net to client
 503 bytes received via SQL*Net from client
 2 SQL*Net roundtrips to/from client
 0 sorts (memory)
 0 sorts (disk)
 1 rows processed
 
果然,扫描常数索引比扫描主键的逻辑读更小一些。考虑到NUMBER类型中,1的存储需要两位,而0的存储只需一位,那么用0代替1创建常数索引,应该效果更好。
 
SQL> CREATE INDEX IND_T_CON_0 ON T(0);
 
索引已创建。
 
SQL> SELECT /*+ INDEX(T IND_T_CON_0) */ COUNT(*) FROM T;
 
COUNT(*)
 ----------
 30931
 
Execution Plan
 ----------------------------------------------------------
 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=26 Card=1)
 1 0 SORT (AGGREGATE)
 2 1 INDEX (FULL SCAN) OF 'IND_T_CON_0' (NON-UNIQUE) (Cost=26 Card=30931)
 
Statistics
 ----------------------------------------------------------
 0 recursive calls
 0 db block gets
 58 consistent gets
 0 physical reads
 0 redo size
 378 bytes sent via SQL*Net to client
 503 bytes received via SQL*Net from client
 2 SQL*Net roundtrips to/from client
 0 sorts (memory)
 0 sorts (disk)
 1 rows processed
 
由于常数索引中所有节点值都相同,如果压缩一下的话,应该还能减少逻辑读。
 
SQL> DROP INDEX IND_T_CON_0;
 
索引已丢弃。
 
SQL> CREATE INDEX IND_T_CON_COMPRESS ON T(0) COMPRESS;
 
索引已创建。
 
SQL> SELECT /*+ INDEX(T IND_T_CON_COMPRESS) */ COUNT(*) FROM T;
 
COUNT(*)
 ----------
 30931
 
Execution Plan
 ----------------------------------------------------------
 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=26 Card=1)
 1 0 SORT (AGGREGATE)
 2 1 INDEX (FULL SCAN) OF 'IND_T_CON_COMPRESS' (NON-UNIQUE) (Cost=26 Card=30931)
 
Statistics
 ----------------------------------------------------------
 0 recursive calls
 0 db block gets
 49 consistent gets
 0 physical reads
 0 redo size
 378 bytes sent via SQL*Net to client
 503 bytes received via SQL*Net from client
 2 SQL*Net roundtrips to/from client
 0 sorts (memory)
 0 sorts (disk)
 1 rows processed
 
和预计的一样,经过压缩,索引扫描的逻辑读进一步减少,现在和最初的主键扫描相比,逻辑读已经减少了30%。
 
如果只为了得到COUNT(*),那么压缩过的常数索引是最佳选择,不过这个索引对其他查询是没有任何帮助的,因此,实际中的用处不大。


    
 
 

您可能感兴趣的文章:

  • 在oracle裡怎麼樣取得數據庫裡的每一個表名?
  • 如何取得ORACLE中的SYNONYMS信息?
  • 如何将从JSP页面中取得的系统当前时间写入oracle数据库表中?
  • Oracle 9i轻松取得建表和索引的DDL语句
  • Oracle9i取得建表和索引的DDL语句
  • 轻松取得Oracle 9i建表和索引DDL语句
  • Oracle轻松取得建表和索引的DDL语句
  • 从jsp想oracle插入记录的顺序问题
  • 紧急求救:为什么oracle只能选择一定数据的记录
  • 记录Linux下一次oracle启动错误
  • ejb的bmp向oracle表插入图片记录的问题!
  • 请问怎么用jsp语句删除oracle中的一条记录?
  • 给200分:oracle的jdbc有BUG??为何在servlet中记录数不能超过120条?
  • 关于JDBC访问Oracle返回数据集的记录限制的问题
  • Oracle 如何快速查找和删除重复记录
  • Oracle 当前用户下所有表的记录总数
  • Oracle中用Rowid查找和删除重复记录
  • 如何确定Oracle数据库表重复的记录
  • shell向oracle插记录 小问题送分了,谢谢
  • 利用ASP来实现Oracle数据记录的分页显示
  • Oracle数据库设置任务计划备份一周的备份记录
  • Linux(Oracle系统在上面)系统无缘无故死机 , 可能是由于应用程序引起 , 可是由于重新启动查不到相关信息 , 不知道在哪里有记录系统CPU Lo
  • MySQL数据迁移到Oracle记录
  • Oracle中取固定记录数详细步骤
  • Oracle基本操作全记录
  • SQL查询前10条记录(SqlServer/mysql/oracle)的语法分析
  • 在oracle下要在同一事务下插入多条记录,该怎么做??最好要有原代码
  •  
    本站(WWW.)旨在分享和传播互联网科技相关的资讯和技术,将尽最大努力为读者提供更好的信息聚合和浏览方式。
    本站(WWW.)站内文章除注明原创外,均为转载、整理或搜集自网络。欢迎任何形式的转载,转载请注明出处。












  • 相关文章推荐
  • linux下安装oracle后使用命令行启动的方法 linux启动oracle
  • ORACLE 中修改用户密码的方法
  • Linux下完全卸载ORACLE 10G的方法
  • 将Oracle 8i数据成功移植Oracle 10g的方法
  • Oracle Connect to Idle Instance解决方法
  • oracle增加表空间大小两种实现方法
  • linux下用ODBC链接Oracle怎么连啊?跪求具体方法!!!
  • Linux系统下查看oracle SID的方法
  • oracle 彻底删除方法
  • window中oracle环境变量设置方法分享
  • Oracle 忘记密码的找回方法
  • oracle的job不能运行问题的解决方法
  • Linux下Oracle 10G DBCA等汉字乱码解决方法
  • Oracle中serveroutput参数一次设置永久保存方法
  • Oracle指定IP访问数据库方法
  • Oracle SID存在解決方法
  • 技术文章 iis7站长之家
  • PB7 连接 Oracle 的设置方法
  • Oracle删除后不能重新安装的解决方法
  • oracle删除文件后数据库启动不了的处理方法
  • Oracle 12c发布简单介绍及官方下载地址
  • 在linux下安装oracle,如何设置让oracle自动启动!也就是让oracle那个服务自动启动,不是手动的
  • oracle 11g最新版官方下载地址
  • 请问su oracle 和su - oracle有什么不同?
  • Oracle 数据库(oracle Database)Select 多表关联查询方式
  • 虚拟机装Oracle R12与Oracle10g
  • Oracle数据库(Oracle Database)体系结构及基本组成介绍
  • Oracle 数据库开发工具 Oracle SQL Developer
  • 如何设置让Oracle SQL Developer显示的时间包含时分秒
  • Oracle EBS R12 支持 Oracle Database 11g
  • Oracle 10g和Oracle 11g网格技术介绍


  • 站内导航:


    特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,