热门标签 | HotTags
当前位置:  开发笔记 > 数据库 > 正文

Oracle取得表中总记录数最快的方法-mysql教程

由于ORACLE的缓存和共享池的机制,SQL语句逻辑读一般从第二次执行才稳定下来,出于篇幅的考虑,下面所有的SELECTCOUNT(*)FROM

由于ORACLE的缓存和共享池的机制,SQL语句逻辑读一般从第二次执行才稳定下来,出于篇幅的考虑,下面所有的SELECT COUNT(*) FROM

查询表中的记录总数的语法就是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(*),那么压缩过的常数索引是最佳选择,不过这个索引对其他查询是没有任何帮助的,因此,实际中的用处不大。


推荐阅读
  • 如何在 PostgreSQL 中查询表创建语句:PostgreSQL 操作指南
    首先,启动 pgAdmin 并连接到目标数据库服务器。接着,在左侧导航栏中展开“架构”节点,找到并选择需要查询的表。最后,切换到“SQL”标签页,即可查看该表的创建语句。此外,你还可以通过执行 `SELECT pg_get_ddl_command('表名'::regclass);` 命令来直接获取表的 DDL 语句。 ... [详细]
  • 在基于.NET框架的分层架构实践中,为了实现各层之间的松散耦合,本文详细探讨了依赖注入(DI)和控制反转(IoC)容器的设计与实现。通过合理的依赖管理和对象创建,确保了各层之间的单向调用关系,从而提高了系统的可维护性和扩展性。此外,文章还介绍了几种常见的IoC容器实现方式及其应用场景,为开发者提供了实用的参考。 ... [详细]
  • SQLSharper 2014 是一款专为 SQL Server Management Studio (SSMS) 设计的功能增强插件,旨在提升 T-SQL 开发者的效率。该插件提供了多种实用工具,包括快速查询数据库对象、详细查看表结构、优化查询结果导出以及自动生成代码等。适用于需要高效管理和开发 SQL 数据库的专业人士。 ... [详细]
  • 如何在Oracle ASM_Diskgroup中重命名现有磁盘
    如何在Oracle ASM_Diskgroup中重命名现有磁盘 ... [详细]
  • 基于Java和JSP的电子医疗记录管理平台
    随着信息技术的快速发展,各类管理系统已在各行各业得到广泛应用。传统的人工管理模式已逐渐无法满足现代需求。本文介绍了一种基于Java和JSP技术开发的电子医疗记录管理平台,旨在提高医疗行业的信息化水平和管理效率。该平台通过整合先进的数据库技术和Web开发框架,实现了医疗记录的高效存储、查询和管理,为医护人员提供了便捷的操作界面和强大的数据支持。 ... [详细]
  • 在数据库管理中,计算字段(也称为计算列)是一种重要的技术手段。计算字段通过在表定义中使用表达式或函数,自动生成并存储计算结果,从而提高查询效率和数据一致性。本文将详细介绍计算字段的创建方法、优化技巧及其在实际应用中的案例,帮助读者更好地理解和运用这一功能。 ... [详细]
  • 在探讨如何高效处理大规模数据报表的分页展示之前,首先需要明确导致报表加载缓慢的主要原因。通常情况下,这主要是由于两个方面:一是查询条件过于宽泛,使得数据库返回的结果集包含数百万甚至更多的记录;二是前端渲染性能不足,无法高效处理大量数据。为了优化这一过程,可以从以下几个方面入手:优化查询条件,减少不必要的数据返回;采用分页查询技术,每次仅加载所需的数据;利用缓存机制,减少对数据库的频繁访问;提升前端渲染效率,使用虚拟滚动等技术提高用户体验。 ... [详细]
  • 最近,我在CentOS 5服务器上成功部署了GForge 5.7 Community Edition。与Advanced Server版本相比,虽然功能略有简化,但仍然能够满足大多数开源项目管理的需求。为了确保数据安全,我开发了一套全自动备份脚本,该脚本能够定期备份GForge的数据和配置文件,并将其存储在远程服务器上,以防止数据丢失。此外,该脚本还具备错误检测和日志记录功能,便于故障排查和维护。 ... [详细]
  • 在CentOS上部署和配置FreeSWITCH
    在CentOS系统上部署和配置FreeSWITCH的过程涉及多个步骤。本文详细介绍了从源代码安装FreeSWITCH的方法,包括必要的依赖项安装、编译和配置过程。此外,还提供了常见的配置选项和故障排除技巧,帮助用户顺利完成部署并确保系统的稳定运行。 ... [详细]
  • Issue with the Reserved Term HOSTS in System Configuration ... [详细]
  • 在开发系统查询搜索功能时,需注意以下几点以提高信息检索效率:首先,在SQL语句中,每个参数占位符“?”后必须紧跟相应的参数赋值,确保参数与赋值一一对应,避免因参数不匹配导致的错误。其次,进行模糊搜索时,若用户输入通配符“%”,可能会导致全表扫描,因此需要对输入的“%”进行特殊处理或限制,以防止不必要的性能开销。此外,建议使用索引优化查询速度,并合理设计搜索逻辑,以提升用户体验。 ... [详细]
  • Spring Boot 实战(一):基础的CRUD操作详解
    在《Spring Boot 实战(一)》中,详细介绍了基础的CRUD操作,涵盖创建、读取、更新和删除等核心功能,适合初学者快速掌握Spring Boot框架的应用开发技巧。 ... [详细]
  • 开发心得:深入探讨Servlet、Dubbo与MyBatis中的责任链模式应用
    开发心得:深入探讨Servlet、Dubbo与MyBatis中的责任链模式应用 ... [详细]
  • 深入解析:Explain命令的应用与字段详解
    深入解析:Explain命令的应用与字段详解 ... [详细]
  • 本文详细解析了如何使用 jQuery 实现一个在浏览器地址栏运行的射击游戏。通过源代码分析,展示了关键的 JavaScript 技术和实现方法,并提供了在线演示链接供读者参考。此外,还介绍了如何在 Visual Studio Code 中进行开发和调试,为开发者提供了实用的技巧和建议。 ... [详细]
author-avatar
田景撩人_108
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有