导读:Oracle的后台运作原理是什么?我们的一条命令是如何被执行的?今天我们就从一条简单的Select语句开始,看看Oracle数据库后台的运作机制。
为盐边等地区用户提供了全套网页设计制作服务,及盐边网站建设行业解决方案。主营业务为网站设计制作、成都网站制作、盐边网站设计,以传统方式定制建设网站,并提供域名空间备案等一条龙服务,秉承以专业、用心的态度为用户提供真诚的服务。我们深信只要达到每一位用户的要求,就会得到认可,从而选择与我们长期合作。这样,我们也可以走得更远!
Select语句可以说是DBA和数据库开发者在工作中使用最多的语句之一,但这条语句是如何执行?在Oracle数据库中又是如何运作的呢?今天我们就从一条简单的Select语句开始,看看Oracle数据库后台的运作机制。这对于我们之后的系统管理与故障排除非常有帮助。
第一步:客户端把语句发给服务器端执行
当我们在客户端执行select语句时,客户端会把这条SQL语句发送给服务器端,让服务器端的进程来处理这语句。也就是说,Oracle客户端是不会做任何的操作,他的主要任务就是把客户端产生的一些SQL语句发送给服务器端。虽然在客户端也有一个数据库进程,但是,这个进程的作用跟服务器上的进程作用事不相同的。服务器上的数据库进程才会对SQL语句进行相关的处理。不过,有个问题需要说明,就是客户端的进程跟服务器的进程是一一对应的。也就是说,在客户端连接上服务器后,在客户端与服务器端都会形成一个进程,客户端上的我们叫做客户端进程;而服务器上的我们叫做服务器进程。所以,由于所有的SQL语句都是服务器进程执行的,所以,有些人把服务器进程形象地比喻成客户端进程的“影子”。
第二步:语句解析
当客户端把SQL语句传送到服务器后,服务器进程会对该语句进行解析。同理,这个解析的工作,也是在服务器端所进行的。虽然这只是一个解析的动作,但是,其会做很多“小动作”。
1. 查询高速缓存。服务器进程在接到客户端传送过来的SQL语句时,不会直接去数据库查询。而是会先在数据库的高速缓存中去查找,是否存在相同语句的执行计划。如果在数据高速缓存中,刚好有其他人使用这个查询语句的话,则服务器进程就会直接执行这个SQL语句,省去后续的工作。所以,采用高速数据缓存的话,可以提高SQL语句的查询效率。一方面是从内存中读取数据要比从硬盘中的数据文件中读取数据效率要高,另一方面,也是因为这个语句解析的原因。
不过这里要注意一点,这个数据缓存跟有些客户端软件的数据缓存是两码事。有些客户端软件为了提高查询效率,会在应用软件的客户端设置数据缓存。由于这些数据缓存的存在,可以提高客户端应用软件的查询效率。但是,若其他人在服务器进行了相关的修改,由于应用软件数据缓存的存在,导致修改的数据不能及时反映到客户端上。从这也可以看出,应用软件的数据缓存跟数据库服务器的高速数据缓存不是一码事。
2. 语句合法性检查。当在高速缓存中找不到对应的SQL语句时,则数据库服务器进程就会开始检查这条语句的合法性。这里主要是对SQL语句的语法进行检查,看看其是否合乎语法规则。如果服务器进程认为这条SQL语句不符合语法规则的时候,就会把这个错误信息,反馈给客户端。在这个语法检查的过程中,不会对SQL语句中所包含的表名、列名等等进行SQL他只是语法上的检查。
3. 语言含义检查。若SQL语句符合语法上的定义的话,则服务器进程接下去会对语句中的字段、表等内容进行检查。看看这些字段、表是否在数据库中。如果表名与列名不准确的话,则数据库会就会反馈错误信息给客户端。
所以,有时候我们写select语句的时候,若语法与表名或者列名同时写错的话,则系统是先提示说语法错误,等到语法完全正确后,再提示说列名或表名错误。若能够掌握这个顺序的话,则在应用程序排错的时候,可以节省时间。
4. 获得对象解析锁。当语法、语义都正确后,系统就会对我们需要查询的对象加锁。这主要是为了保障数据的一致性,防止我们在查询的过程中,其他用户对这个对象的结构发生改变。对于加锁的原理与方法,我在其他文章中已经有专门叙述,在这里就略过不谈了。
5. 数据访问权限的核对。当语法、语义通过检查之后,客户端还不一定能够取得数据。服务器进程还会检查,你所连接的用户是否有这个数据访问的权限。若你连接上服务器的用户不具有数据访问权限的话,则客户端就不能够取得这些数据。故,有时候我们查询数据的时候,辛辛苦苦地把SQL语句写好、编译通过,但是,最后系统返回个 “没有权限访问数据”的错误信息,让我们气半死。这在前端应用软件开发调试的过程中,可能会碰到。所以,要注意这个问题,数据库服务器进程先检查语法与语义,然后才会检查访问权限。
6. 确定最佳执行计划。当语句与语法都没有问题,权限也匹配的话,服务器进程还是不会直接对数据库文件进行查询。服务器进程会根据一定的规则,对这条语句进行优化。不过要注意,这个优化是有限的。一般在应用软件开发的过程中,需要对数据库的sql语言进行优化,这个优化的作用要大大地大于服务器进程的自我优化。所以,一般在应用软件开发的时候,数据库的优化是少不了的。
当服务器进程的优化器确定这条查询语句的最佳执行计划后,就会将这条SQL语句与执行计划保存到数据高速缓存。如此的话,等以后还有这个查询时,就会省略以上的语法、语义与权限检查的步骤,而直接执行SQL语句,提高SQL语句处理效率。
第三步:语句执行
语句解析只是对SQL语句的语法进行解析,以确保服务器能够知道这条语句到底表达的是什么意思。等到语句解析完成之后,数据库服务器进程才会真正的执行这条SQL语句。
这个语句执行也分两种情况。一是若被选择行所在的数据块已经被读取到数据缓冲区的话,则服务器进程会直接把这个数据传递给客户端,而不是从数据库文件中去查询数据。若数据不在缓冲区中,则服务器进程将从数据库文件中查询相关数据,并把这些数据放入到数据缓冲区中。
这里仍然要注意一点,就是Oracle数据库中,定义了很多种类的高速缓存。像上面所说的SQL语句缓存与现在讲的数据缓存。我们在学习数据库的时候,需要对这些缓存有一个清晰的认识,并了解各个种类缓存的作用。这对于我们后续数据库维护与数据库优化是非常有用的。
第四步:提取数据
当语句执行完成之后,查询到的数据还是在服务器进程中,还没有被传送到客户端的用户进程。所以,在服务器端的进程中,有一个专门负责数据提取的一段代码。他的作用就是把查询到的数据结果返回给用户端进程,从而完成整个查询动作。
从这整个查询处理过程中,我们在数据库开发或者应用软件开发过程中,需要注意以下几点:
一是要了解数据库缓存跟应用软件缓存是两码事情。数据库缓存只有在数据库服务器端才存在,在客户端是不存在的。只有如此,才能够保证数据库缓存中的内容跟数据库文件的内容一致。才能够根据相关的规则,防止数据脏读、错读的发生。而应用软件所涉及的数据缓存,由于跟数据库缓存不是一码事情,所以,应用软件的数据缓存虽然可以提高数据的查询效率,但是,却打破了数据一致性的要求,有时候会发生脏读、错读等情况的发生。所以,有时候,在应用软件上有专门一个功能,用来在必要的时候清除数据缓存。不过,这个数据缓存的清除,也只是清除本机上的数据缓存,或者说,只是清除这个应用程序的数据缓存,而不会清除数据库的数据缓存。
二是绝大部分SQL语句都是按照这个处理过程处理的。我们DBA或者基于Oracle数据库的开发人员了解这些语句的处理过程,对于我们进行涉及到SQL语句的开发与调试,是非常有帮助的。有时候,掌握这些处理原则,可以减少我们排错的时间。特别要注意,数据库是把数据查询权限的审查放在语法语义的后面进行检查的。所以,有时会若光用数据库的权限控制原则,可能还不能满足应用软件权限控制的需要。此时,就需要应用软件的前台设置,实现权限管理的要求。而且,有时应用数据库的权限管理,也有点显得繁琐,会增加服务器处理的工作量。因此,对于记录、字段等的查询权限控制,大部分程序涉及人员喜欢在应用程序中实现,而不是在数据库上实现。
Oracle认证全面解析
Oracle是第一个跨整个产品线(数据库、业务应用软件和应用软件开发与决策支持工具)开发和部署100%基于互联网的企业软件的公司。下面是我整理的关于Oracle认证管理员认证考试,希望大家认真阅读!
一、 Oracle,仅次于微软的世界第二大软件公司
Oracle虽然规模很大,但名声不像微软、IBM那样显赫,很多非计算机专业的在校学生不知道Oracle是何物。但是如果你是一位想在毕业后进入IT行业的学生,那么,你必须知道什么是Oracle.
首先,Oracle是一家软件公司。这家1977成立于加利福尼亚的软件公司是世界上第一个推出关系型数据管理系统(RDBMS)的公司。现在,他们的RDBMS被广泛应用于各种操作环境:Windows NT、基于UNIX系统的小型机、IBM大型机以及一些专用硬件操作系统平台。事实上,Oracle已经成为世界上最大的RDBMS供应商,并且是世界上最主要的信息处理软件供应商。现在,Oracle是仅次于微软公司的世界第二大软件公司,2000年的销售额为101亿美元。现在Oracle在世界范围内大约聘用了4万3千多名专业技术人员,在美国本土有2万1千名。有趣的是,即使在美国本土,Oracle的技术人员(主要是程序员)中有40%是印度人,中国人是第二多的,其次才是美国人。
然后,Oracle是一个庞大的品牌系统。如前所述,Oracle不仅在全球最先推出了RDBMS,并且事实上掌握着这个市场的大部分份额,由于Oracle 公司的RDBMS都以Oracle为名,所以,在某种程度上Oracle己经成为了RDBMS的代名词。而近几年来,Oracle不断积极地扩展自己的业务,成功地开发了许多横向和纵向的产品,譬如Oracle自己设计生产的Oracle服务器,面对商业客户的商业交易系统(Oracle Exchange),Oracle数据中心等等。同时,Oracle还成功开发出了很多面对程序员的开发工具,譬如著名的Designer/2000计算机辅助系统工程(CASE)工具和Developer/2000开发包,这些工具作为“设计器”和“开发器”,有力地帮助了程序员的开发工作,也有力地提高了Oracle的声誉。此外,Oracle还开发了一系列面对不同对象的应用软件,它们被统称为Oracle应用软件。这些软件使得Oracle被广泛应用到各个领域,同时也为Oracle带来滚滚财源。比较常见的Oracle软件产品包括:。Oracle财务软件(Oracle Financial)。Oracle制造业软件(Oracle Manufacturing)。Oracle人力资源软件(Oracle Human Resources)。Oracle自动控制软件(Oracle Automotive)。Oracle 商业交易系统(Oracle Exchange)
以及其他的应用软件。这些软件连同OracleRDBMS使得Oracle成为一个庞大的品牌系统,深入到了人们生产和生活的各个领域。
由于Oracle拥有多年在各种行业下的多种解决方案,拥有基于不同客户环境的Oracle RDMBS服务器工具及规模巨大的应用软件系统,而且,这个庞大的系统正在不同程度、不同层次地为各行各业所广泛应用。因此,能够熟练掌握这个系统的某些方面及适应这个系统的不断更新的专业工程师必然是各界急需的人才,有着良好的就业前景。事实上,Oracle公司也认识到了这一点,这就是Oracle认证专家——OCP(Oracle Certified Professional)的由来。
二、Oracle认证种类介绍
Oracle认证专家——OCP,是由Oracle公司授权国际考试认证中心对考生进行的资格认证。考生按考试标准要求参加几门课程的.考试(一般为3—5门),在通过全部考试后,便可获得OCP的专家认证。
目前OCP认证考试分为:
Database Administrator:数据库管理员考试认证,简称DBA.数据库管理员负责对数据库进行日常的管理、备份及数据库崩溃后的恢复问题。
Database Operator:数据库操作员认证考试,简称DBO.数据库操作员主要是基于Windows NT的Oracle 8数据库管理,能够熟练应用OEM等工具完成对数据库的操作及日常的管理工作。
Database Developer:数据库开发员认证考试,简称DEV.数据库开发员应能熟练掌握用Developer/2000的工具建立各种Forms应用程序,建立各种标准的以及自定义的报表。
Java Developer:Java开发人员考试。
Application Consultant: Oracle产品应用咨询顾问。
其中,Oracle DBA是最吃香,但也是最难考的一个认证。在Oracle的官方的网站上,对DBA有以下说明:
Oracle DBA专家可以跟上如今日趋复杂的系统环境要求。最好的DBA们都在幕后工作,他们小心地维护着系统,使得系统可以每天都平稳地运转,并且防止意外灾难的发生,譬如数据库崩溃或者成小时地宕机。这项艰巨的任务需要对Oracle数据库的结构和运行方式有着广泛泛深入的了解,并且有丰富的实战经验。最好的DBA可以在取得最佳运行状态及防止他们公司停止运行的突发事件中找到平衡。Oracle DBA认证考试就是提供一个证明该人可以胜任Oracle DBA这一职务的认证。本认证考试设计了5门独立的考试,使你可以利用良好的知识获得一个专家认证。
三、如何参加考试?
OCP认证的所有考试也是通过Prometric公司组织的,具体的考试事宜请访问它的官方的网站:
目前OCP每门考试的费用为125美元。
四、如何准备考试?
1、Oracle公司推荐的官方准备途径
一般来讲,Oracle的考试内容比较多,并且考题也很细,对动手能力要求很强。因此,为了有足够的把握,一定要提前充分准备,而且一定要注重动手实践。在Oracle官方的网站上推荐的通过认证的途径如下,可以进行参考。
Oracle大学:Oracle大学提供的教师引导及基于技术的训练(Instructor-led training and technology- based training)是准备OCP认证的最好方式,这些课程将为你打下需要通过OCP认证的知识基础。你可以查阅一下课程表来选择理想的准备方式,你当地的Oracle大学可以在这方面给你一些最好的建议。你可以访问Oracle的网站,获得更多的信息。
自己准备:实战经验是加深你对Oracle考试内容理解的最好方式。Oracle建议你将理论学习拓展,在实际工作或是练习中使用新学到的技巧和知识来自学。
考试内容检查表:使用考试内容检查表(Test Content Checklist)来确定你必须准备的所有题目。Oracle会不断地更新考试内容检查表,所以请访问Oracle网站 ,下载最新的考试指南。
其它考试工具:模拟考试题及自我测试软件可以帮助你更好地准备OCP认证考试。Oracle和自我测试软件公司(Self Test Software)联合出品了一些高质量的考试软件以帮助考生更好地准备OCP认证考试,这些软件可以通过 Oracle 网站 定购。
事实上,绝大部分的大陆同学都是通过参加培训班来准备考试的,它们不仅辅导通过考试的知识,而且其他信息包括如何报名,考场上如何应考,都可以得到辅导。以上Oracle官方的介绍只是一般地对你进行一下有关备考的初级教育,事实上,正像我们前面说过的,作为一个庞大的数据库系统,要想掌握好Oracle(更不要说完全掌握了——我甚至怀疑是否有人真正完全掌握过Oracle),从任何角度而言都不是一件易事。除非你有足够的决心和耐心,准备将Oracle当作你的职业之路并致力于数据库技术的研究,你才有可能真正了解这个庞杂系统的精髓。以下的一些论述将有助于你对Oracle有一个初步的了解。
2、Oracle数据库技术基本知识
(1)Oracle数据库涵盖了数据库技术的方方面面。
尽管关系型数据库从原理上来讲并不是很难,有一些数据库知识的人掌握这些原理并不会花很大的气力,而且,一般的关系型数据库软件,譬如 SQL Server,用起来并不复杂,但Oracle绝对与众不同。作为有史以来最成功的关系型数据库软件,Oracle的实现方式和管理维护手段非常丰富,它涉及到很多方面的细节技术。单从任何一个侧面来理解它,是难观其全貌的。要想真的精通它,没有两三年功夫是不行的。单是Oracle的技术文档,就足够淹没你的! 具体说来,Oracle技术包括以下几个主要方面:Oracle系统结构和原理、Oracle数据库的安装和配置、Oracle数据库的管理、Oracle的数据备份与恢复技术、Oracle的性能调整、Oracle的新产品特性(Java支持、应用服务器、时间空间系列、文本服务等)、Oracle的并行服务器技术、Oracle的数据仓库技术、Oracle的对象类型和对象一关系模型等技术……
毫不夸张地说,以上任何一个部分单独拿出来都是很大的题目!
(2)Oracle技术发展快,技术的关联性大。
尽管Oracle只是一个数据库系统,但在实际应用中,你不可能只面对数据库本身。譬如,你在做Oracle数据库的网络管理,你将不可避免地遇到如何与网络通讯、防火墙的技术兼容的问题。同理,你在做其它方面的工作,也肯定有方方面面的技术关联问题需要解决。而且更要命的是,这些技术(包括Oracle本身)发展速度都非常快,这就使得跟上它们的发展成为一件十分费劲的事情。
不过幸运的是,我们并不需要同时了解Oracle的所有方面,Oracle提供了很多应用方向,我们可以按照偏好,选择其中的一个方向,集中时间和精力,努力在这个方向做到有所成就还不是一件很难的事情。由于Oracle目前已是世界上最主要的数据库供应商和第二大软件公司,Oracle的数据库技术已经被广泛应用于各个领域,因而市场上对Oracle人才的需求量是相当之大,获得OCP认证不愁找不到工作。而以后我们可以在工作中学以致用,再努力地钻研Oracle数据库及相关技术。
;
1. 预估执行计划 - Explain Plan
Explain plan以SQL语句作为输入,得到这条SQL语句的执行计划,并将执行计划输出存储到计划表中。
首先,在你要执行的SQL语句前加explain plan for,此时将生成的执行计划存储到计划表中,语句如下:
explain plan for SQL语句
然后,在计划表中查询刚刚生成的执行计划,语句如下:
select * from table(dbms_xplan.display);
注意:Explain plan只生成执行计划,并不会真正执行SQL语句,因此产生的执行计划有可能不准,因为:
1)当前的环境可能和执行计划生成时的环境不同;
2)不会考虑绑定变量的数据类型;
3)不进行变量窥视。
2. 查询内存中缓存的执行计划 (dbms_xplan.display_cursor)
如果你想获取正在执行的或刚执行结束的SQL语句真实的执行计划(即获取library cache中的执行计划),可以到动态性能视图里查询。方法如下:
1)获取SQL语句的游标
游标分为父游标和子游标,父游标由sql_id(或联合address和hash_value)字段表示,子游标由child_number字段表示。
如果SQL语句正在运行,可以从v$session中获得它的游标信息,如:
select status, sql_id, sql_child_number from v$session where status='ACTIVE' and ....
如果知道SQL语句包含某些关键字,可以从v$sql视图中获得它的游标信息,如:
select sql_id, child_number, sql_text from v$sql where sql_text like '%关键字%‘
2)获取库缓存中的执行计划
为了获取缓存库中的执行计划,可以直接查询动态性能视图v$sql_plan和v$sql_plan_statistics_all等,但更方便的方法是以sql_id和子游标为参数,执行如下语句:
select * from table(dbms_xplan.display_cursor('sql_id',child_number));
3)获取前一次的执行计划:
set serveroutput off
select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));
3. 查询历史执行计划(dbms_xplan.display_awr)
AWR会定时把动态性能视图中的执行计划保存到dba_hist_sql_plan视图中,如果你想要查看历史执行计划,可以采用如下方法查询:
select * from table(dbms_xplan.display_awr('sql_id');
4. 在用sqlplus做SQL开发是(Autotrace)
set autotrace是sqlplus工具的一个功能,只能在通过sqlplus连接的session中使用,它非常适合在开发时测试SQL语句的性能,有以下几种参数可供选择:
SET AUTOTRACE OFF ---------------- 不显示执行计划和统计信息,这是缺省模式
SET AUTOTRACE ON EXPLAIN ------ 只显示优化器执行计划
SET AUTOTRACE ON STATISTICS -- 只显示统计信息
SET AUTOTRACE ON ----------------- 执行计划和统计信息同时显示
SET AUTOTRACE TRACEONLY ------ 不真正执行,只显示预期的执行计划,同explain plan
5. 生成Trace文件查询详细的执行计划 (SQL_Trace, 10046)
SQL_TRACE作为初始化参数可以在实例级别启用,也可以只在会话级别启用,在实例级别启用SQL_TRACE会导致所有进程的活动被跟踪,包括后台进程及所有用户进程,这通常会导致比较严重的性能问题,所以在一般情况下,我们使用sql_trace跟踪当前进程,方法如下:
SQLalter session set sql_trace=true;
...被跟踪的SQL语句...
SQLalter session set sql_trace=false;
如果要跟踪其它进程,可以通过Oracle提供的系统包DBMS_SYSTEM. SET_SQL_TRACE_IN_SESSION来实现,例如:
SQL exec dbms_system.set_sql_trace_in_session(sid,serial#,true) --开始跟踪
SQL exec dbms_system.set_sql_trace_in_session(sid,serial#,false) --结束跟踪
生成trace文件后,再用tkprof 工具将sql trace 生成的跟踪文件转换成易读的格式,语法如下:
tkprof inputfile outputfile
10046事件是SQL_TRACE的一个升级版,它也是追踪会话,生成Trace文件,只是它里面的内容更详细
以oracle表分析为例:
drop table test;
select count(*) from test;
--创建测试表
create table test
(
id number(9),
nick varchar2(30)
);
--插入测试数据
begin
for i in 1..100000 loop
insert into test(id) values(i);
end loop;
commit;
end;
select * from test;
--更新nick字段,使数据发生严重倾斜
update test set nick='abc' where rownum99999;
--创建索引
create index idx_test_nick on test(nick);
update test set nick='def' where nick is null;
--只对索引进行分析
analyze index idx_test_nick compute statistics;
select * from user_indexes;
--查看索引名,对应存储的数据块,不同的key数量,记录数(行数)的分析信息
select index_name, LEAF_BLOCKS, DISTINCT_KEYS, NUM_ROWS
from user_indexes
where index_name = 'IDX_TEST_NICK';
--dba_tab_col_statistics
--查看表的统计信息
select COLUMN_NAME, NUM_BUCKETS, num_distinct
from USER_tab_columns
where table_name = 'TEST';
select * from test where nick ='abc';
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'TEST'
2 1 INDEX (RANGE SCAN) OF 'IDX_TEST_NICK' (NON-UNIQUE)
select * from test where nick ='def';
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'TEST'
2 1 INDEX (RANGE SCAN) OF 'IDX_TEST_NICK' (NON-UNIQUE)
--由上可以看到,对索引分析之后,sql的执行路径都是基于规则的,索引的字段的偏移
--先根据索引找到rowid,然后再根据rowid读取记录,这个过程肯定比全表扫描读取记录要慢
--user_part_col_statistics 分区分析信息
--分析表的第二列nick
analyze table test compute statistics for columns size 2 nick;
select * from test where nick ='abc';
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'TEST'
2 1 INDEX (RANGE SCAN) OF 'IDX_TEST_NICK' (NON-UNIQUE)
--根据上面的执行计划,还是按照规则来执行的
--分析表
analyze table test compute statistics for table;
select * from test where nick ='abc';
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=49 Card=99998 Bytes=
1499970)
1 0 TABLE ACCESS (FULL) OF 'TEST' (Cost=49 Card=99998 Bytes=14
99970)
--分析表之后,完全按照成本来执行
--删除所有的统计数据,并只对表与列进行分析,不分析索引,
--ORACLE使用CBO的优化器,并产生了正确的执行计划
analyze table test delete statistics;
--分析列nick
analyze table test compute statistics for table for columns size 2 nick;
select * from test where nick ='abc';
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=49 Card=99998 Bytes=
1499970)
1 0 TABLE ACCESS (FULL) OF 'TEST' (Cost=49 Card=99998 Bytes=14
99970)
--
select * from test where nick ='def';
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=2 Bytes=30)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'TEST' (Cost=2 Card=2 Byt
es=30)
2 1 INDEX (RANGE SCAN) OF 'IDX_TEST_NICK' (NON-UNIQUE) (Cost
=1 Card=2)
--创建TEST表ID列上的索引,但不对索引进行分析
create index idx_test_id on test(id);
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1000 Bytes=15
000)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'TEST' (Cost=2 Card=1000
Bytes=15000)
2 1 INDEX (RANGE SCAN) OF 'IDX_TEST_ID' (NON-UNIQUE) (Cost=1
Card=400)
--当条件中即有id,又有nick时,因为nick上有直方图,ORACLE知道nick='abc'的值特别的多,所以不走IDX_TEST_NICK索引,走IDX_TEST_ID上的索引
select * from test where id=5 and nick='abc';
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1000 Bytes=15
000)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'TEST' (Cost=2 Card=1000
Bytes=15000)
2 1 INDEX (RANGE SCAN) OF 'IDX_TEST_ID' (NON-UNIQUE) (Cost=1
Card=400)
--当条件中即有id,又有nick时,因为nick上有直方图,ORACLE知道nick='def'的值特别的少,所以走IDX_TEST_NICK上的索引,不走IDX_TEST_ID索引
select * from test where id=5 and nick='def';
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=15)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'TEST' (Cost=2 Card=1 Byt
es=15)
2 1 INDEX (RANGE SCAN) OF 'IDX_TEST_NICK' (NON-UNIQUE) (Cost
=1 Card=2)
select * from test where nick='def' and id=5;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=15)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'TEST' (Cost=2 Card=1 Byt
es=15)
2 1 INDEX (RANGE SCAN) OF 'IDX_TEST_NICK' (NON-UNIQUE) (Cost
=1 Card=2)
--在分析ID列后,ORACLE发现ID列的选择度更高,所以不再选择IDX_TEST_NICK索引,而是选择IDX_TEST_ID
analyze table test compute statistics for columns size 1 id;
select * from test where id=5 and nick='def';
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=7)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'TEST' (Cost=2 Card=1 Byt
es=7)
2 1 INDEX (RANGE SCAN) OF 'IDX_TEST_ID' (NON-UNIQUE) (Cost=1
Card=1)
/*
下面来看另外一种情况,我们删除所有的统计数据,然后在ID列上创建唯一索引,在此条件下,
只分析表与分析列nick,我们看到ORACLE走了正确的执行计划,
走了UK_TEST_ID,其实从这里也给我们带来很多的启示:
在主键与唯一键约束的列上是否需要直方图的问题?
如果在这些列上有像这样的查询where id 100 and id 1000,
我们还是需要有直方图的,但除此之外,好像真的没有直方图的必要了!
*/
analyze table test delete statistics;
drop index idx_test_id;
create unique index uk_test_id on test(id);
--分析表的第二列nick
analyze table test compute statistics for table for columns size 2 nick;
select * from test where id=5 and nick='def';
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=15)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'TEST' (Cost=2 Card=1 Byt
es=15)
2 1 INDEX (UNIQUE SCAN) OF 'UK_TEST_ID' (UNIQUE) (Cost=1 Car
d=100000)
从以上一系列的实验可以看出,对ORACLE的优化器CBO来说,表的分析与列的分析才是最重要的,索引的分析次之。还有我们可以考虑我们的哪些列上需要直方图,对于bucket的个数问题,oracle的默认值是75个,所以根据你的应用规则,选择合适的桶数对性能也是有帮助的。因为不必要的桶的个数的大量增加,必然会带来SQL语句硬解析时产生执行计划的复杂度问题。
实现思路:可以通过w3c的dom解析器进行操作,之后通过getName获取到xmltpye中的属性值。
举例:
import java.io.File;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
public class DomTest1
{
public static void main(String[] args) throws Exception
{
// step 1: 获得dom解析器工厂(工作的作用是用于创建具体的解析器)
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
// System.out.println("class name: " + dbf.getClass().getName());
// step 2:获得具体的dom解析器
DocumentBuilder db = dbf.newDocumentBuilder();
// System.out.println("class name: " + db.getClass().getName());
// step3: 解析一个xml文档,获得Document对象(根结点)
Document document = db.parse(new File("candidate.xml"));
NodeList list = document.getElementsByTagName("PERSON");
for(int i = 0; i list.getLength(); i++)
{
Element element = (Element)list.item(i);
String content = element.getElementsByTagName("NAME").item(0).getFirstChild().getNodeValue();
System.out.println("name:" + content);
System.out.println("--------------------------------------");
}
}
}