当前位置: 首页 > 编程日记 > 正文

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

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

相关文章:

你的创业前行之路,亲君愿与你一路相随

每一个创业者都是黑暗中的独行侠, 然后告诉别人光明就在前方, 其实, 他们自己心里也不知道光明还有多远。 无论当初是因为理想还是因为实现个人价值, 只要走上创业这条路, 我们就只能不断的告诉自己和他人:…

年度重磅:《AI聚变:2018年优秀AI应用案例TOP 20》正式发布

2018 年,AI 行业的关键词或许非“落地”二字莫属 ,人们强烈期待着更多 AI 技术应用和深入商业化。 一方面,科技巨头们在横向铺设 AI 技术平台,但也更强调 AI 与每一个垂直行业的深度融合。而另一面,AI 创业公司在频繁刷…

防止熊猫烧香的微软补丁

为了防止熊猫烧香病毒,要及时安装微软的安全更新,不要随意访问来源不明的网站。特别是微软的MS06-014漏洞,应立即打好该漏洞补丁。 不知道这个补丁是不是已经打上的朋友,一是在控制面板里可以看到.(勾上显示更新前面的方框),或者下载360安全卫士,扫描漏洞…

【Azure Services Platform Step by Step-第11篇】Windows Azure兰州拉面馆-日志与队列的使用...

在第9篇里,为了便于大家理解,我把Windows Azure的环境比喻成了"Azure兰州拉面馆"。本篇我们继续沿用这个比喻,讲讲Windows Azure中的队列(Queue Storage)与日志的使用。 Queue Storage在【Azure Services Platform Step by Step-第…

这才是世界排名前十位的奢侈品

​​真正的奢侈品,不是豪车大宅,不是名包贵表,而是这些…… 1. 一颗童心2. 生生不息的信念3. 背包走天下的健康体魄4. 愉悦和舒心的工作环境5. 安稳平和的睡眠6. 享受属于自己空间与时间的生活7. 牵手一个教会你爱与被爱的人8. 品味美好的心情…

配置springmvc在其他类中(spring容器外)获取注入bean

今天在写JedisUtils的时候要注入JedisPool,而这个属性被设置为static,Resource和Autowired都不可以注入,因为spring不能为静态变量依赖注入。因此需要额外的方法获取spring管理的bean。本文即SpringContextHolder: 1 package com.…

声智科技完成2亿元B轮融资,将持续拓展语音交互产品的规模化落地

整理 | 一一出品 | AI科技大本营寒冬之下,不少创业公司依然得到了资本青睐。AI科技大本营消息,12 月 29 日消息,声智科技(SoundAI)已于近期完成 2 亿人民币 B 轮融资,本轮投资由毅达资本领投,峰…

360度看IT行业--3月12日西安工程大学讲座

2009年3月12日,一个绿色的好日子。当天晚上7点30分,在西安工程大学的讲座开始了。西安工程大学的孩子们很热情,大一大二的居多,虽然是非计算机专业,但提前就满满的坐在会场等待了。而且很关注IT行业,提了很…

专访中国移动钱岭:大数据更像是一种“倍增器”

记者 | 杨丽出品 | AI科技大本营(rgznai100)为把握时代特征,2016 年中国移动确定并大力推动“大连接”战略,并制定了“十三五”时期做大连接规模、做优连接服务、做强连接应用的三个目标。如今,这家企业已经拥有 9.16 亿移动端用户…

创业者应该如何看待金钱

什么是真正的赢?一个高智商、高情商的商人,经营企业追求的终极目标就是一个赢字:金钱、地位、豪宅、名车、美女、权利、荣耀,为了赢得这些,很多人曾不择手段,曾不断地苦苦寻找“如何以最小的投入快速获得最…

Exchange与ADFS单点登录 PART 2:部署和配置ADFS

在第一篇文章完了之后,我们就可以在我们的服务器上部署ADFS了,安装的方法很简单,直接在服务器管理器中添加功能角色即可,选择当前服务器并在服务器角色中选择ADFS。 完成之后我们需要对ADFS进行详细的配置,在服务器管理…

路由器和交换机的综合实验

路由器和交换机的综合实验<?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office" />主要目标&#xff1a;1. 跨交换机&#xff08;二层交换机&#xff09;的相同VLAN 之间的通讯2. 不同VLAN之间的通讯1&#xff09; 通过路由器实现不同VL…

如何写出符合Python审美的代码风格?

作者 | Rocky0429来源 | Python空间本文为 AI科技大本营投稿文章&#xff08;欢迎给我们投稿&#xff0c;投稿请联系微信1092722531&#xff09;写在之前每个人都有自己的代码风格&#xff0c;随着写的行数增加&#xff0c;自己对于代码的审美也会变的不一样&#xff0c;这就像…

程序员都该懂点 HTTP

作者&#xff1a;developerHaozGithub 地址&#xff1a;developerHaoz说明&#xff1a;本文主要是对 HTTP 基础知识进行总结和归纳&#xff0c;毕竟做 IT 的&#xff0c;网络这一块还是很重要的 本文的主要内容HTTP 是什么URL 详解HTTP 之请求篇HTTP 之响应篇一、HTTP是什么1、…

谁的青春不迷茫,其实我们都一样

如果你为人生画一条浅浅吃苦底线&#xff0c;就别妄想跨越深邃的幸福极限。在熠熠生辉前&#xff0c;总要捱过一段孤独不安的日子。唯有担得起厚重的经历&#xff0c;才能禁得起岁月推敲。记住&#xff1a;一定要努力&#xff0c;但别着急。加油&#xff01; 很庆幸你能以这样好…

关于Iframe在IE6下不显示的bug

IE都出到IE8了,用IE6的人渐渐少了..但还是存在的.例如QAMM们在用. 所以,IE6下存在的问题也必须解决.这两天,我就遇到一个了: html<table> <tr> <td id"tdTest" runat"server"> <iframe id"ifrTest" h…

腾讯优图吴永坚:迈向深度学习,我们面临模型训练与推荐的双重考验

整理 | 琥珀出品 | AI 科技大本营对腾讯优图的发展历程&#xff0c;吴永坚表示&#xff0c;优图是非常幸运的&#xff0c;幸运的同时也知道优图选对了方向&#xff0c;只要坚持&#xff0c;还是会有收获的。12 月 15 日&#xff0c;以"新趋势、新技术、新应用"为主题…

JS子窗口调用父窗口中的函数

很简单只需要一句话就可以了&#xff1a; window.opener.changeColor(); 这里的changeColor()就是父窗口中JS的一个函数 本文转自sucre03 51CTO博客&#xff0c;原文链接&#xff1a;http://blog.51cto.com/sucre/377011&#xff0c;如需转载请自行联系原作者

父亲节遇上端午节,你难道不回家吗?

创业者是孤独的 他们选择了更加艰险的人生。 同时&#xff0c;他们又是幸运的 因为他们背后有一位伟大的父亲。 决定创业时 他说&#xff1a;“大胆去闯吧&#xff0c;有爸爸在” 创业失败时 他说&#xff1a;“累了就回家&#xff0c;有爸爸在” 简单朴实的话语包含的是无私广…

蜘蛛爬虫网络高像素图片抓取工具[搜索引擎]

ZSpider—— 是一款Photo crawler工具。主要功能&#xff1a;免费抓取网络高像素图片&#xff0c;并下载到本地。使用说明&#xff1a;1. 软件环境&#xff1a;Windows XP, 20003, Vista, 2008, Windows 7.NET Framework 3.52. 双击Spider.exe&#xff0c;选择菜单&#xff0c;…

技术流 | 手把手教你用Python设计一个命令行界面

作者 | Yannick Wolff 译者 | 刘旭坤 整理 | Jane出品 | Python大本营对 Python 程序来说&#xff0c;完备的命令行界面可以提升团队的工作效率&#xff0c;减少调用时可能碰到的困扰。今天&#xff0c;我们就来教大家如何设计功能完整的 Python 命令行界面。对 …

送给那些还在迷茫的人

在现在这个高节奏的高效率的时代&#xff0c;两级分化特别厉害。有钱的会越来越有钱&#xff0c;没钱的要么安于现状&#xff0c;要么就越来越穷。  这种思维的产生根源就是不懂得把握机会&#xff0c;其实机会这个东西很奇妙&#xff0c;有时候我们自己也能发现有个机会就摆…

cisco路由器与QOS技术

1 前言随着因特网的普及&#xff0c;网络和人们生活的关系愈加密切&#xff0c;多样化的业务&#xff08;数据、语音、视频等&#xff09;应运而生。通常&#xff0c;这些业务对于通信条件的要求各不相同&#xff1a;数据业务对传递的可靠性要求非常高&#xff0c;误码率是最重…

shell介绍,命令历史,命令补全和别名 ,通配符, 输入输出重定向

2019独角兽企业重金招聘Python工程师标准>>> shell介绍 shell是一个命令解释器&#xff0c;提供用户和机器之间的交互&#xff1b; 支持特定语法&#xff0c;比如逻辑判断、循环&#xff1b; 每个用户都可以有自己特定的shell&#xff1b; CentOS7默认shell为bash (…

2018年Python开源项目Top100!只在这里!

整理 | Jane出品 | Python大本营2018 年的最后一天&#xff0c;营长为大家新鲜出炉了一份 2018 年 Python 开源项目 Top100 清单&#xff01;这些项目都是营长每月通过收集 Mybridge 的数据整理而来的&#xff0c;是不是很棒&#xff01;&#xff08;我要在留言区看到你们夸我~…

亲君圆梦,创业一起来

你想像中的创业&#xff0c;是不是办公地点找好了吗&#xff1f;需要注册公司一步一步来呢做哪个行业好呢有没有什么赚钱的项目呢没有你想的那么困难只需要一部手机发个链接点一下 玩一年 创业不花一分钱 校园创业 只需动动手指想要带你一起赚钱一起飞吗&#xff1f;我在厦门亲…

半途而废的香山之旅

今天终于下定决心去香山了&#xff01;大清早&#xff0c;就和同学小燕两个人买好了路上吃的东西。顶着北京早晨凉爽但有点阻的风骑车自行车向香山的方向进发了!这是我第一次去香山&#xff0c;路线也只能是摸索了&#xff01;我本来是准备从北四环西路一直往西骑&#xff0c;然…

POwershell 更改文件权限

今天需要给某个网络共享的大文件重新配置一个权限。这个文件夹下面有很多乱七八糟的小文件&#xff0c;很多创建人甚至已经离开公司了。如果一个个地目录手动修改所有者权限&#xff0c;再打开继承关系&#xff0c;这样比较麻烦&#xff0c;这个时候自然是用脚本比较方便了。 1…

“清华帮”AI造富

作者 | 马程&#xff1b;编辑 | 罗丽娟来源 | 全天候科技【导语】如今的AI创业者中&#xff0c;有一大批人出自精英汇聚的 “姚班”和清华的各个院系。他们们凭借技术傍身迅速打造出一批AI独角兽&#xff0c;但在发展过程中&#xff0c;他们又不得不面对资本的压力&#xff0c;…

什么时候是创业最佳时机?7个最佳的励志创业时机GET了吗

1、年轻时人们常说“出名要趁早”&#xff0c;创业也是同样的道理&#xff0c;创业越早&#xff0c;成功的几率就越大。年轻是一种巨大的财富&#xff0c;是无知和天真的完美结合&#xff0c;吸取经验可以作为愚蠢决定的借口。有人说&#xff0c;和职业篮球运动员一样&#xff…