您好,欢迎访问三七文档
当前位置:首页 > IT计算机/网络 > 数据库 > Oracle SQL 性能优化
OracleSQL性能优化1.选择最有效率的表名顺序例如:表TAB116,384条记录表TAB21条记录选择TAB2作为基础表(最好的方法)selectcount(*)fromtab1,tab2执行时间0.96秒选择TAB1作为基础表(不佳的方法)selectcount(*)fromtab2,tab1执行时间26.09秒如果有3个以上的表连接查询,那就需要选择交叉表(intersectiontable)作为基础表,交叉表是指那个被其他表所引用的表.例如:EMP表描述了LOCATION表和CATEGORY表的交集.SELECT*FROMLOCATIONL,CATEGORYC,EMPEWHEREE.EMP_NOBETWEEN1000AND2000ANDE.CAT_NO=C.CAT_NOANDE.LOCN=L.LOCN将比下列SQL更有效率SELECT*FROMEMPE,LOCATIONL,CATEGORYCWHEREE.CAT_NO=C.CAT_NOANDE.LOCN=L.LOCNANDE.EMP_NOBETWEEN1000AND20002.WHERE子句中的连接顺序ORACLE采用自下而上的顺序解析WHERE子句,根据这个原理,表之间的连接必须写在其他WHERE条件之前,那些可以过滤掉最大数量记录的条件必须写在WHERE子句的末尾.例如:(低效,执行时间156.3秒)SELECT…FROMEMPEWHERESAL50000ANDJOB=‘MANAGER'AND25(SELECTCOUNT(*)FROMEMPWHEREMGR=E.EMPNO);(高效,执行时间10.6秒)SELECT…FROMEMPEWHERE25(SELECTCOUNT(*)FROMEMPWHEREMGR=E.EMPNO)ANDSAL50000ANDJOB=‘MANAGER';3.SELECT子句中避免使用‘*‘4.用Where子句替换HAVING子句避免使用HAVING子句,HAVING只会在检索出所有记录之后才对结果集进行过滤.这个处理需要排序,总计等操作.如果能通过WHERE子句限制记录的数目,那就能减少这方面的开销.例如:低效:SELECTREGION,AVG(LOG_SIZE)FROMLOCATIONGROUPBYREGIONHAVINGREGIONREGION!=‘SYDNEY'ANDREGION!=‘PERTH'高效SELECTREGION,AVG(LOG_SIZE)FROMLOCATIONWHEREREGIONREGION!=‘SYDNEY'ANDREGION!=‘PERTH'GROUPBYREGION5.减少对表的查询在含有子查询的SQL语句中,要特别注意减少对表的查询.例如:低效SELECTTAB_NAMEFROMTABLESWHERETAB_NAME=(SELECTTAB_NAMEFROMTAB_COLUMNSWHEREVERSION=604)ANDDB_VER=(SELECTDB_VERFROMTAB_COLUMNSWHEREVERSION=604)高效SELECTTAB_NAMEFROMTABLESWHERE(TAB_NAME,DB_VER)=(SELECTTAB_NAME,DB_VER)FROMTAB_COLUMNSWHEREVERSION=604)Update多个Column例子:低效:UPDATEEMPSETEMP_CAT=(SELECTMAX(CATEGORY)FROMEMP_CATEGORIES),SAL_RANGE=(SELECTMAX(SAL_RANGE)FROMEMP_CATEGORIES)WHEREEMP_DEPT=0020;高效:UPDATEEMPSET(EMP_CAT,SAL_RANGE)=(SELECTMAX(CATEGORY),MAX(SAL_RANGE)FROMEMP_CATEGORIES)WHEREEMP_DEPT=0020;6.使用表的别名当在SQL语句中连接多个表时,请使用表的别名并把别名前缀于每个Column上.这样一来,就可以减少解析的时间并减少那些由Column歧义引起的语法错误.7.用EXISTS替代IN在许多基于基础表的查询中,为了满足一个条件,往往需要对另一个表进行联接.在这种情况下,使用EXISTS(或NOTEXISTS)通常将提高查询的效率.低效:SELECT*FROMEMP(基础表)WHEREEMPNO0ANDDEPTNOIN(SELECTDEPTNOFROMDEPTWHERELOC=‘MELB')高效:SELECT*FROMEMP(基础表)WHEREEMPNO0ANDEXISTS(SELECT‘X'FROMDEPTWHEREDEPT.DEPTNO=EMP.DEPTNOANDLOC=‘MELB')8.用NOTEXISTS替代NOTIN在子查询中,NOTIN子句将执行一个内部的排序和合并.无论在哪种情况下,NOTIN都是最低效的(因为它对子查询中的表执行了一个全表遍历).为了避免使用NOTIN,我们可以把它改写成外连接(OuterJoins)或NOTEXISTS.例如:SELECT…FROMEMPWHEREDEPT_NONOTIN(SELECTDEPT_NOFROMDEPTWHEREDEPT_CAT='A');为了提高效率.改写为:(方法一:高效)SELECT….FROMEMPA,DEPTBWHEREA.DEPT_NO=B.DEPT(+)ANDB.DEPT_NOISNULLANDB.DEPT_CAT(+)=‘A'(方法二:最高效)SELECT….FROMEMPEWHERENOTEXISTS(SELECT‘X'FROMDEPTDWHERED.DEPT_NO=E.DEPT_NOANDDEPT_CAT=‘A');9.用表连接替换EXISTS通常来说,采用表连接的方式比EXISTS更有效率SELECTENAMEFROMEMPEWHEREEXISTS(SELECT‘X'FROMDEPTWHEREDEPT_NO=E.DEPT_NOANDDEPT_CAT=‘A');(更高效)SELECTENAMEFROMDEPTD,EMPEWHEREE.DEPT_NO=D.DEPT_NOANDDEPT_CAT=‘A';10.用EXISTS替换DISTINCT当提交一个包含一对多表信息(比如部门表和雇员表)的查询时,避免在SELECT子句中使用DISTINCT.一般可以考虑用EXIST替换例如:低效:SELECTDISTINCTDEPT_NO,DEPT_NAMEFROMDEPTD,EMPEWHERED.DEPT_NO=E.DEPT_NO高效:SELECTDEPT_NO,DEPT_NAMEFROMDEPTDWHEREEXISTS(SELECT‘X'FROMEMPEWHEREE.DEPT_NO=D.DEPT_NO);EXISTS使查询更为迅速,因为RDBMS核心模块将在子查询的条件一旦满足后,立刻返回结果.11.基础表的选择基础表(DrivingTable)是指被最先访问的表(通常以全表扫描的方式被访问).根据优化器的不同,SQL语句中基础表的选择是不一样的.如果你使用的是CBO(COSTBASEDOPTIMIZER),优化器会检查SQL语句中的每个表的物理大小,索引的状态,然后选用花费最低的执行路径.如果你用RBO(RULEBASEDOPTIMIZER),并且所有的连接条件都有索引对应,在这种情况下,基础表就是FROM子句中列在最后的那个表.举例:SELECTA.NAME,B.MANAGERFROMWORKERA,LODGINGBWHEREA.LODGING=B.LODING;由于LODGING表的LODING列上有一个索引,而且WORKER表中没有相比较的索引,WORKER表将被作为查询中的基础表.12.用UNION替换OR(适用于索引列)通常情况下,用UNION替换WHERE子句中的OR将会起到较好的效果.对索引列使用OR将造成全表扫描.注意,以上规则只针对多个索引列有效.如果有column没有被索引,查询效率可能会因为你没有选择OR而降低.在下面的例子中,LOC_ID和REGION上都建有索引.高效:SELECTLOC_ID,LOC_DESC,REGIONFROMLOCATIONWHERELOC_ID=10UNIONSELECTLOC_ID,LOC_DESC,REGIONFROMLOCATIONWHEREREGION=“MELBOURNE”低效:SELECTLOC_ID,LOC_DESC,REGIONFROMLOCATIONWHERELOC_ID=10ORREGION=“MELBOURNE”如果你坚持要用OR,那就需要返回记录最少的索引列写在最前面.注意:WHEREKEY1=10(返回最少记录)ORKEY2=20(返回最多记录)ORACLE内部将以上转换为WHEREKEY1=10AND((NOTKEY1=10)ANDKEY2=20)13.用IN来替换OR下面的查询可以被更有效率的语句替换:低效:SELECT….FROMLOCATIONWHERELOC_ID=10ORLOC_ID=20ORLOC_ID=30高效SELECT…FROMLOCATIONWHERELOC_ININ(10,20,30);14.避免在索引列上使用ISNULL和ISNOTNULL避免在索引中使用任何可以为空的列,ORACLE将无法使用该索引.对于单列索引,如果列包含空值,索引中将不存在此记录.对于复合索引,如果每个列都为空,索引中同样不存在此记录.如果至少有一个列不为空,则记录存在于索引中.举例:如果唯一性索引建立在表的A列和B列上,并且表中存在一条记录的A,B值为(123,null),ORACLE将不接受下一条具有相同A,B值(123,null)的记录(插入).然而如果所有的索引列都为空,ORACLE将认为整个键值为空而空不等于空.因此你可以插入1000条具有相同键值的记录,当然它们都是空!因为空值不存在于索引列中,所以WHERE子句中对索引列进行空值比较将使ORACLE停用该索引.举例:低效:(索引失效)SELECT…FROMDEPARTMENTWHEREDEPT_CODEISNOTNULL;高效:(索引有效)SELECT…FROMDEPARTMENTWHEREDEPT_CODE=0;15.总是使用索引的第一个列16.用WHERE替代ORDERBYORDERBY子句只在两种严格的条件下使用索引.ORDERBY中所有的列必须包含在相同的索引中并保持在索引中的排列顺序.ORDERBY中所有的列必须定义为非空.WHERE子句使用的索引和ORDERBY子句中所使用的索引不能并列.例如:表DEPT包含以下列:DEPT_CODEPKNOTNULLDEPT_DESCNOTNULLDEPT_TYPENULL非唯一性的索引(DEPT_TYPE)低效:(索引不被使用)SELECTDEPT_CODEFROMDEPTORDERBYDEPT_TYPEEXPLAINPLAN:SORTORDERBYTABLEACCESSFULL高效:(使用索引)SELECTDEPT_CODEFROMDEPTWHEREDEPT_TYPE0EXPLAINPLAN:TABLEACCESSBYROWIDONEMPINDEXRANGESCANONDEPT_IDX译者按:ORDERBY也能使用索引!这的确是个容易被忽视的知识点.我们来验证一下:16.需要当心的WHERE子句某些SELECT语句中的WHERE子句不使用索引.这里有一些例子.在下面的例子里,‘!='将不使用索引.记住,索引只能告诉你什么存在于表中,而不能告诉你什么不存在于表中.不使用索引:SELECTACCOUNT_NAMEFROMTRANSACTIONWHEREAMOUNT!=0;使用索引:SELECTACCOUNT_NAMEFROMTRANSACTIONWHEREAMOUNT0;下面的例子中,‘|
本文标题:Oracle SQL 性能优化
链接地址:https://www.777doc.com/doc-8738 .html