高效SQL语句必杀技
No SQL,No cost. SQL语句是造成数据库开销最大的部分。而不良SQL写法直接导致数据库系统性能下降的情形比比皆是。那么如何才能称得
上高效的SQL语句呢?一是查询优化器为当前的SQL语句生成最佳的执行计划,保证数据读写使用最佳路径;二是设置合理的物理存储结构,如表
的类型,字段的顺序,字段的数据类型等。本文主要描述如何编写高效的SQL语句并给出示例。下面的描述主要分为三个部分,一是编写高效SQL
语句,二是使用索引提高查询性能的部分,三是总结部分。
一、编写高效SQL语句
- 1)选择最有效的表名顺序(仅适用于RBO模式)
- ORACLE的解析器总是按照从右到左的顺序处理FROM子句中的表名,因此FROM子句中最后的一个表将作为驱动表被优先处理。当FROM子句
- 存在多个表的时候,应当考虑将表上记录最少的那个表置于FROM的最右端作为基表。Oracle会首先扫描基表(FROM子句中最后的那个表)并对
- 记录进行排序,然后扫描第二个表(FROM子句中最后第二个表),最后将所有从第二个表中检索出的记录与第一个表中合适记录进行合并。如
- 果有3个以上的表连接查询,那就需要选择交叉表(intersectiontable)作为基础表,交叉表是指那个被其他表所引用的表。
- 下面的例子使用最常见的scott或hr模式下的表进行演示
- 表EMP有14条记录
- 表DEPT有4条记录
- SELECT/*+rule*/COUNT(*)FROMemp,dept;--高效的写法
- scott@CNMMBO>setautotracetraceonlystat;
- scott@CNMMBO>SELECT/*+rule*/COUNT(*)FROMemp,dept;
- Elapsed:00:00:00.14
- Statistics
- ----------------------------------------------------------
- 1recursivecalls
- 0dbblockgets
- 35consistentgets
- 0physicalreads
- 0redosize
- 515bytessentviaSQL*Nettoclient
- 492bytesreceivedviaSQL*Netfromclient
- 2SQL*Netroundtripsto/fromclient
- 0sorts(memory)
- 0sorts(disk)
- 1rowsprocessed
- SELECT/*+rule*/COUNT(*)FROMdept,emp;--低效的写法
- scott@CNMMBO>SELECT/*+rule*/COUNT(*)FROMdept,emp;
- Elapsed:00:00:00.02
- Statistics
- ----------------------------------------------------------
- 1recursivecalls
- 0dbblockgets
- 105consistentgets
- 0physicalreads
- 0redosize
- 515bytessentviaSQL*Nettoclient
- 492bytesreceivedviaSQL*Netfromclient
- 2SQL*Netroundtripsto/fromclient
- 0sorts(memory)
- 0sorts(disk)
- 1rowsprocessed
- 2)select查询中避免使用'*'
- 当你想在SELECT子句中列出所有的COLUMN时,使用动态SQL列引用'*'是一个方便的方法.不幸的是,这是一个非常低效的方法.实际
- 上,ORACLE在解析的过程中,会将'*'依次转换成所有的列名,这个工作是通过查询数据字典完成的,这意味着将耗费更多的时间。
- 注:本文中的例子出于简化演示而使用了select*,生产环境应避免使用.
- 3)减少访问数据库的次数
- 每当执行一条SQL语句,Oracle需要完成大量的内部操作,象解析SQL语句,估算索引的利用率,绑定变量,读数据块等等.由此可
- 见,减少访问数据库的次数,实际上是降低了数据库系统开销
- -->下面通过3种方式来获得雇员编号为7788与7902的相关信息
- -->方式1(最低效):
- selectename,job,salfromempwhereempno=7788;
- selectename,job,salfromempwhereempno=7902;
- -->方式2(次低效):
- -->下面使用了参数游标来完成,每传递一次参数则需要对表emp访问一次,增加了I/O
- DECLARE
- CURSORC1(E_NONUMBER)IS
- SELECTename,job,sal
- FROMemp
- WHEREempno=E_NO;
- BEGIN
- OPENC1(7788);
- FETCHC1INTO…,…,…;
- ..
- OPENC1(7902);
- FETCHC1INTO…,…,…;
- CLOSEC1;
- END;
- -->方式3(最高效)
- SELECTa.ename
- ,a.job
- ,a.sal
- ,b.ename
- ,b.job
- ,b.sal
- FROMempa,empb
- WHEREa.empno=7788ORb.empno=7902;
- 注意:在SQL*Plus,SQL*Forms和Pro*C中重新设置ARRAYSIZE参数,可以增加每次数据库访问的检索数据量,建议值为200.
- 4)使用DECODE函数来减少处理时间
- -->使用decode函数可以避免重复扫描相同的行或重复连接相同的表
- selectcount(*),sum(sal)fromempwheredeptno=20andenamelike'SMITH%';
- selectcount(*),sum(sal)fromempwheredeptno=30andenamelike'SMITH%';
- -->通过使用decode函数一次扫描即可完成所有满足条件记录的处理
- SELECTCOUNT(DECODE(deptno,20,'x',NULL))d20_count
- ,COUNT(DECODE(deptno,30,'x',NULL))d30_count
- ,SUM(DECODE(deptno,20,sal,NULL))d20_sal
- ,SUM(DECODE(deptno,30,sal,NULL))d30_sal
- FROMemp
- WHEREenameLIKE'SMITH%';
- 类似的,DECODE函数也可以运用于GROUPBY和ORDERBY子句中。
- 5)整合简单,无关联的数据库访问
- -->如果你有几个简单的数据库查询语句,你可以把它们整合到一个查询中以提高性能(即使它们之间没有关系)
- -->整合前
- SELECTname
- FROMemp
- WHEREempno=1234;
- SELECTname
- FROMdept
- WHEREdeptno=10;
- SELECTname
- FROMcat
- WHEREcat_type='RD';
- -->整合后
- SELECTe.name,d.name,c.name
- FROMcatc
- ,dptd
- ,empe
- ,dualx
- WHERENVL('X',x.dummy)=NVL('X',e.ROWID(+))
- ANDNVL('X',x.dummy)=NVL('X',d.ROWID(+))
- ANDNVL('X',x.dummy)=NVL('X',c.ROWID(+))
- ANDe.emp_no(+)=1234
- ANDd.dept_no(+)=10
- ANDc.cat_type(+)='RD';
- -->从上面的SQL语句可以看出,尽管三条语句被整合为一条,性能得以提高,然可读性差,此时应权衡性能与代价
- 6)删除重复记录
- -->通过使用rowid来作为过滤条件,性能高效
- DELETEFROMempe
- WHEREe.ROWID>(SELECTMIN(x.ROWID)
- FROMempx
- WHEREx.empno=e.empno);
- 7)使用truncate代替delete
- -->通常情况下,任意记录的删除需要在回滚段构造删除前镜像以实现回滚(rollback).对于未提交的数据在执行rollback之后,Oracle会生成
- -->等价SQL语句去恢复记录(如delete,则生成对应的insert语句;如insert则生成对应的delete;如update,则是同时生成delete和insert
- -->使用truncate命令则是执行DDL命令,不产生任何回滚信息,直接格式化并释放高水位线.故该语句性能高效.由于不能rollback,因此慎用.
- 8)尽量多使用COMMIT(COMMIT应确保事务的完整性)
- -->只要有可能,在程序中尽量多使用COMMIT,这样程序的性能得到提高,需求也会因为COMMIT所释放的资源而减少
- -->COMMIT所释放的资源:
- -->1.回滚段上用于恢复数据的信息
- -->2.释放语句处理期间所持有的锁
- -->3.释放redologbuffer占用的空间(commit将redologbuffer中的entries写入到联机重做日志文件)
- -->4.ORACLE为管理上述3种资源中的内部开销
- 9)计算记录条数
- -->一般的情况下,count(*)比count(1)稍快.如果可以通过索引检索,对索引列的计数是最快的,因为直接扫描索引即可,例如COUNT(EMPNO)
- -->实际情况是经测试上述三种情况并无明显差异.
- 10)用Where子句替换HAVING子句
- -->尽可能的避免having子句,因为HAVING子句是对检索出所有记录之后再对结果集进行过滤。这个处理需要排序,总计等操作
- -->通过WHERE子句则在分组之前即可过滤不必要的记录数目,从而减少聚合的开销
- -->低效:
- SELECTdeptno,AVG(sal)
- FROMemp
- GROUPBYdeptno
- HAVINGdeptno=20;
- scott@CNMMBO>SELECTdeptno,AVG(sal)
- 2FROMemp
- 3GROUPBYdeptno
- 4HAVINGdeptno=20;
- Statistics
- ----------------------------------------------------------
- 0recursivecalls
- 0dbblockgets
- 7consistentgets
- 0physicalreads
- 0redosize
- 583bytessentviaSQL*Nettoclient
- 492bytesreceivedviaSQL*Netfromclient
- 2SQL*Netroundtripsto/fromclient
- 0sorts(memory)
- 0sorts(disk)
- 1rowsprocessed
- -->高效:
- SELECTdeptno,AVG(sal)
- FROMemp
- WHEREdeptno=20
- GROUPBYdeptno;
- scott@CNMMBO>SELECTdeptno,AVG(sal)
- 2FROMemp
- 3WHEREdeptno=20
- 4GROUPBYdeptno;
- Statistics
- ----------------------------------------------------------
- 0recursivecalls
- 0dbblockgets
- 2consistentgets
- 0physicalreads
- 0redosize
- 583bytessentviaSQL*Nettoclient
- 492bytesreceivedviaSQL*Netfromclient
- 2SQL*Netroundtripsto/fromclient
- 0sorts(memory)
- 0sorts(disk)
- 1rowsprocessed
- 11)最小化表查询次数
- -->在含有子查询的SQL语句中,要特别注意减少对表的查询
- -->低效:
- SELECT*
- FROMemployees
- WHEREdepartment_id=(SELECTdepartment_id
- FROMdepartments
- WHEREdepartment_name='Marketing')
- ANDmanager_id=(SELECTmanager_id
- FROMdepartments
- WHEREdepartment_name='Marketing');
- -->高效:
- SELECT*
- FROMemployees
- WHERE(department_id,manager_id)=(SELECTdepartment_id,manager_id
- FROMdepartments
- WHEREdepartment_name='Marketing')
- -->类似更新多列的情形
- -->低效:
- UPDATEemployees
- SETjob_id=(SELECTMAX(job_id)FROMjobs),salary=(SELECTAVG(min_salary)FROMjobs)
- WHEREdepartment_id=10;
- -->高效:
- UPDATEemployees
- SET(job_id,salary)=(SELECTMAX(job_id),AVG(min_salary)FROMjobs)
- WHEREdepartment_id=10;
- 12)使用表别名
- -->在多表查询时,为所返回列使用表别名作为前缀以减少解析时间以及那些相同列歧义引起的语法错误
- 13)用EXISTS替代IN
- 在一些基于基础表的查询中,为了满足一个条件,往往需要对另一个表进行联接.在这种情况下,使用EXISTS(或NOTEXISTS)通常
- 将提高查询的效率.
- -->低效:
- SELECT*
- FROMemp
- WHEREsal>1000
- ANDdeptnoIN(SELECTdeptno
- FROMdept
- WHEREloc='DALLAS')
- -->高效:
- SELECT*
- FROMemp
- WHEREempno>1000
- ANDEXISTS
- (SELECT1
- FROMdept
- WHEREdeptno=emp.deptnoANDloc='DALLAS')
- 14)用NOTEXISTS替代NOTIN
- 在子查询中,NOTIN子句引起一个内部的排序与合并.因此,无论何时NOTIN子句都是最低效的,因为它对子查询中的表执行了一个全表
- 遍历.为避免该情形,应当将其改写成外部连接(OUTTERJOIN)或适用NOTEXISTS
- -->低效:
- SELECT*
- FROMemp
- WHEREdeptnoNOTIN(SELECTdeptno
- FROMdept
- WHEREloc='DALLAS');
- -->高效:
- SELECTe.*
- FROMempe
- WHERENOTEXISTS
- (SELECT1
- FROMdept
- WHEREdeptno=e.deptnoANDloc='DALLAS');
- -->最高效(尽管下面的查询最高效,并不推荐使用,因为列loc使用了不等运算,当表dept数据量较大,且loc列存在索引的话,则此时索引失效)
- SELECTe.*
- FROMempeLEFTJOINdeptdONe.deptno=d.deptno
- WHEREd.loc<>'DALLAS'
- 15)使用表连接替换EXISTS
- 一般情况下,使用表连接比EXISTS更高效
- -->低效:
- SELECT*
- FROMemployeese
- WHEREEXISTS
- (SELECT1
- FROMdepartments
- WHEREdepartment_id=e.department_idANDdepartment_name='IT');
- -->高效:
- SELECT*-->经测试此写法SQLplus下比上面的写法多一次逻辑读,而在Toad下两者结果一致
- FROMemployeeseINNERJOINdepartmentsdONd.department_id=e.department_id
- WHEREd.department_name='IT';
- 16)用EXISTS替换DISTINCT
- 对于一对多关系表信息查询时(如部门表和雇员表),应避免在select子句中使用distinct,而使用exists来替换
- -->低效:
- SELECTDISTINCTe.department_id,d.department_name
- FROMdepartmentsdINNERJOINemployeeseONd.department_id=e.department_id;
- -->高效:
- SELECTd.department_id,department_name
- fromdepartmentsd
- WHEREEXISTS
- (SELECT1
- FROMemployeese
- WHEREd.department_id=e.department_id);
- EXISTS使查询更为迅速,因为RDBMS核心模块将在子查询的条件一旦满足后,立刻返回结果
- -->经测试此写法SQLplus下比上面的写法多一次逻辑读,而在Toad下两者结果一致
- 17)使用UNIONALL替换UNION(如果有可能的话)
- 当SQL语句需要UNION两个查询结果集时,这两个结果集合会以UNION-ALL的方式被合并,然后在输出最终结果前进行排序。
- 如果用UNIONALL替代UNION,这样排序就不是必要了。效率就会因此得到提高。
- 注意:
- UNIONALL会输出所有的结果集,而UNION则过滤掉重复记录并对其进行排序.因此在使用时应考虑业务逻辑是否允许当前的结果集存在重复现象
- 寻找低效的SQL语句
- -->下面的语句主要适用于从视图v$sqlarea中获得当前运行下且耗用buffer_gets较多的SQL语句
- SELECTexecutions
- ,disk_reads
- ,buffer_gets
- ,ROUND((buffer_gets
- -disk_reads)
- /buffer_gets,2)
- hit_ratio
- ,ROUND(disk_reads/executions,2)reads_per_run
- ,sql_text
- FROMv$sqlarea
- WHEREexecutions>0
- ANDbuffer_gets>0
- AND(buffer_gets
- -disk_reads)
- /buffer_gets<0.80
- ORDERBY4DESC;
- 18)尽可能避免使用函数,函数会导致更多的recursivecalls
二、合理使用索引以提高性能
索引依赖于表而存在,是真实表的一个缩影,类似于一本书的目录,通过目录以更快获得所需的结果。Oracle使用了一个复杂的自平衡
B数据结构。即任意记录的DML操作将打破索引的平衡,而定期重构索引使得索引重新获得平衡。通常,通过索引查找数据比全表扫描更高效。
任意的DQL或DML操作,SQL优化引擎优先使用索引来计算当前操作的成本以生成最佳的执行计划。一旦使用索引操出参数optimizer_index_cost_adj
设定的值才使用全表扫描。同样对于多表连接使用索引也可以提高效率。同时索引也提供主键(primary key)的唯一性验证。
除了那些LONG或LONG RAW数据类型,你可以索引几乎所有的列.通常,在大型表中使用索引特别有效.当然,你也会发现,在扫描小表时,使用索
引同样能提高效率。
虽然使用索引能得到查询效率的提高,但是索引需要空间来存储,需要定期维护.尤其是在有大量DML操作的表上,任意的DML操作都将引起索
引的变更这意味着每条记录的INSERT , DELETE , UPDATE将为此多付出4 , 5 次的磁盘I/O . 因为索引需要额外的存储空间和处理,
那些不必要的索引反而会使查询反应时间变慢。
DML操作使用索引上存在碎片而失去高度均衡,因此定期的重构索引是有必要的.
- 1)避免基于索引列的计算
- where子句中的谓词上存在索引,而此时基于该列的计算将使得索引失效
- -->低效:
- SELECTemployee_id,first_name
- FROMemployees
- WHEREemployee_id+10>150;-->索引列上使用了计算,因此索引失效,走全表扫描方式
- -->高效:
- SELECTemployee_id,first_name
- FROMemployees
- WHEREemployee_id>160;-->走索引范围扫描方式
- 例外情形
- 上述规则不适用于SQL中的MIN和MAX函数
- hr@CNMMBO>SELECTMAX(employee_id)max_id
- 2FROMemployees
- 3WHEREemployee_id
- 4+10>150;
- 1rowselected.
- ExecutionPlan
- ----------------------------------------------------------
- Planhashvalue:1481384439
- ---------------------------------------------------------------------------------------------
- |Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|
- ---------------------------------------------------------------------------------------------
- |0|SELECTSTATEMENT||1|4|1(0)|00:00:01|
- |1|SORTAGGREGATE||1|4|||
- |2|FIRSTROW||5|20|1(0)|00:00:01|
- |*3|INDEXFULLSCAN(MIN/MAX)|EMP_EMP_ID_PK|5|20|1(0)|00:00:01|
- ---------------------------------------------------------------------------------------------
- 2)避免在索引列上使用NOT运算或不等于运算(<>,!=)
- 通常,我们要避免在索引列上使用NOT或<>,两者会产生在和在索引列上使用函数相同的影响。当ORACLE遇到NOT或不等运算时,他就会停止
- 使用索引转而执行全表扫描。
- -->低效:
- SELECT*
- FROMemp
- WHERENOT(deptno=20);-->实际上NOT(deptno=20)等同于deptno<>20,即deptno<>同样会限制索引
- -->高效:
- SELECT*
- FROMemp
- WHEREdeptno>20ORdeptno<20;
- -->尽管此方式可以替换且实现上述结果,但依然走全表扫描,如果是单纯的>或<运算,则此时为索引范围扫描
- 需要注意的是,在某些时候,ORACLE优化器会自动将NOT转化成相对应的关系操作符
- 其次如果是下列运算符进行NOT运算,依然有可能选择走索引,仅仅除了NOT=之外,因为NOT=等价于<>
- “NOT>”to<=
- “NOT>=”to<
- “NOT<”to>=
- “NOT<=”to>
- 来看一个实际的例子
- hr@CNMMBO>SELECT*
- 2FROMemployees
- 3wherenotemployee_id<100;-->索引列上使用了not,但是该查询返回了所有的记录,即107条,因此此时选择走全表扫描
- 107rowsselected.
- ExecutionPlan
- ----------------------------------------------------------
- Planhashvalue:1445457117
- -------------------------------------------------------------------------------
- |Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|
- -------------------------------------------------------------------------------
- |0|SELECTSTATEMENT||107|7276|3(0)|00:00:01|
- |*1|TABLEACCESSFULL|EMPLOYEES|107|7276|3(0)|00:00:01|-->执行计划中使用了走全表扫描方式
- -------------------------------------------------------------------------------
- PredicateInformation(identifiedbyoperationid):
- ---------------------------------------------------
- 1-filter("EMPLOYEE_ID">=100)-->查看这里的谓词信息被自动转换为>=运算符
- hr@CNMMBO>SELECT*
- 2FROMemployees
- 3wherenotemployee_id<140;-->此例与上面的语句相同,仅仅是查询范围不同返回67条记录,而此时选择了索引范围扫描
- 67rowsselected.
- ExecutionPlan
- ----------------------------------------------------------
- Planhashvalue:603312277
- ---------------------------------------------------------------------------------------------
- |Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|
- ---------------------------------------------------------------------------------------------
- |0|SELECTSTATEMENT||68|4624|3(0)|00:00:01|
- |1|TABLEACCESSBYINDEXROWID|EMPLOYEES|68|4624|3(0)|00:00:01|
- |*2|INDEXRANGESCAN|EMP_EMP_ID_PK|68||1(0)|00:00:01|-->索引范围扫描方式
- ---------------------------------------------------------------------------------------------
- PredicateInformation(identifiedbyoperationid):
- ---------------------------------------------------
- 2-access("EMPLOYEE_ID">=140)
- 3)用UNION替换OR(适用于索引列)
- 通常情况下,使用UNION替换WHERE子句中的OR将会起到较好的效果.基于索引列使用OR使得优化器倾向于使用全表扫描,而不是扫描索引.
- 注意,以上规则仅适用于多个索引列有效。如果有column没有被索引,查询效率可能会因为你没有选择OR而降低。
- -->低效:
- SELECTdeptno,dname
- FROMdept
- WHEREloc='DALLAS'ORdeptno=20;
- -->高效:
- SELECTdeptno,dname
- FROMdept
- WHEREloc='DALLAS'
- UNION
- SELECTdeptno,dname
- FROMdept
- WHEREdeptno=30
- -->经测试,由于数据量较少,此时where子句中的谓词上都存在索引列时,两者性能相当.
- -->假定where子句中存在两列
- scott@CNMMBO>createtablet6asselectobject_id,owner,object_namefromdba_objectswhereowner='SYS'andrownum<1001;
- scott@CNMMBO>insertintot6selectobject_id,owner,object_namefromdba_objectswhereowner='SCOTT'andrownum<6;
- scott@CNMMBO>createindexi_t6_object_idont6(object_id);
- scott@CNMMBO>createindexi_t6_owneront6(owner);
- scott@CNMMBO>insertintot6selectobject_id,owner,object_namefromdba_objectswhereowner='SYSTEM'andrownum<=300;
- scott@CNMMBO>commit;
- scott@CNMMBO>execdbms_stats.gather_table_stats('SCOTT','T6',cascade=>true);
- scott@CNMMBO>selectowner,count(*)fromt6groupbyowner;
- OWNERCOUNT(*)
- ------------------------------
- SCOTT5
- SYSTEM300
- SYS1000
- scott@CNMMBO>select*fromt6whereowner='SCOTT'andrownum<2;
- OBJECT_IDOWNEROBJECT_NAME
- --------------------------------------------------
- 69450SCOTTT_TEST
- scott@CNMMBO>select*fromt6whereobject_id=69450orowner='SYSTEM';
- 301rowsselected.
- ExecutionPlan
- ----------------------------------------------------------
- Planhashvalue:238853296
- -----------------------------------------------------------------------------------------------
- |Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|
- -----------------------------------------------------------------------------------------------
- |0|SELECTSTATEMENT||300|7200|5(0)|00:00:01|
- |1|CONCATENATION||||||
- |2|TABLEACCESSBYINDEXROWID|T6|1|24|2(0)|00:00:01|
- |*3|INDEXRANGESCAN|I_T6_OBJECT_ID|1||1(0)|00:00:01|
- |*4|TABLEACCESSBYINDEXROWID|T6|299|7176|3(0)|00:00:01|
- |*5|INDEXRANGESCAN|I_T6_OWNER|300||1(0)|00:00:01|
- -----------------------------------------------------------------------------------------------
- PredicateInformation(identifiedbyoperationid):
- ---------------------------------------------------
- 3-access("OBJECT_ID"=69450)
- 4-filter(LNNVL("OBJECT_ID"=69450))
- 5-access("OWNER"='SYSTEM')
- Statistics
- ----------------------------------------------------------
- 0recursivecalls
- 0dbblockgets
- 46consistentgets
- 0physicalreads
- 0redosize
- 11383bytessentviaSQL*Nettoclient
- 712bytesreceivedviaSQL*Netfromclient
- 22SQL*Netroundtripsto/fromclient
- 0sorts(memory)
- 0sorts(disk)
- 301rowsprocessed
- scott@CNMMBO>select*fromt6whereowner='SYSTEM'orobject_id=69450;
- 301rowsselected.
- ExecutionPlan
- ----------------------------------------------------------
- Planhashvalue:238853296
- -----------------------------------------------------------------------------------------------
- |Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|
- -----------------------------------------------------------------------------------------------
- |0|SELECTSTATEMENT||300|7200|5(0)|00:00:01|
- |1|CONCATENATION||||||
- |2|TABLEACCESSBYINDEXROWID|T6|1|24|2(0)|00:00:01|
- |*3|INDEXRANGESCAN|I_T6_OBJECT_ID|1||1(0)|00:00:01|
- |*4|TABLEACCESSBYINDEXROWID|T6|299|7176|3(0)|00:00:01|
- |*5|INDEXRANGESCAN|I_T6_OWNER|300||1(0)|00:00:01|
- -----------------------------------------------------------------------------------------------
- PredicateInformation(identifiedbyoperationid):
- ---------------------------------------------------
- 3-access("OBJECT_ID"=69450)
- 4-filter(LNNVL("OBJECT_ID"=69450))
- 5-access("OWNER"='SYSTEM')
- Statistics
- ----------------------------------------------------------
- 1recursivecalls
- 0dbblockgets
- 46consistentgets
- 0physicalreads
- 0redosize
- 11383bytessentviaSQL*Nettoclient
- 712bytesreceivedviaSQL*Netfromclient
- 22SQL*Netroundtripsto/fromclient
- 0sorts(memory)
- 0sorts(disk)
- 301rowsprocessed
- scott@CNMMBO>select*fromt6
- 2whereobject_id=69450
- 3union
- 4select*fromt6
- 5whereowner='SYSTEM';
- 301rowsselected.
- ExecutionPlan
- ----------------------------------------------------------
- Planhashvalue:370530636
- ------------------------------------------------------------------------------------------------
- |Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|
- ------------------------------------------------------------------------------------------------
- |0|SELECTSTATEMENT||301|7224|7(72)|00:00:01|
- |1|SORTUNIQUE||301|7224|7(72)|00:00:01|
- |2|UNION-ALL||||||
- |3|TABLEACCESSBYINDEXROWID|T6|1|24|2(0)|00:00:01|
- |*4|INDEXRANGESCAN|I_T6_OBJECT_ID|1||1(0)|00:00:01|
- |5|TABLEACCESSBYINDEXROWID|T6|300|7200|3(0)|00:00:01|
- |*6|INDEXRANGESCAN|I_T6_OWNER|300||1(0)|00:00:01|
- ------------------------------------------------------------------------------------------------
- PredicateInformation(identifiedbyoperationid):
- ---------------------------------------------------
- 4-access("OBJECT_ID"=69450)
- 6-access("OWNER"='SYSTEM')
- Statistics
- ----------------------------------------------------------
- 1recursivecalls
- 0dbblockgets
- 7consistentgets
- 0physicalreads
- 0redosize
- 11383bytessentviaSQL*Nettoclient
- 712bytesreceivedviaSQL*Netfromclient
- 22SQL*Netroundtripsto/fromclient
- 1sorts(memory)
- 0sorts(disk)
- 301rowsprocessed
- -->从上面的统计信息可知,consistentgets由46下降为7,故当where子句中谓词上存在索引时,使用union替换or更高效
- -->即使当列object_id与owner上不存在索引时,使用union仍然比or更高效(在Oracle10gR2与Oracle11gR2测试)
- 4)避免索引列上使用函数
- -->下面是一个来自实际生产环境的例子
- -->表acc_pos_int_tbl上business_date列存在索引,由于使用了SUBSTR函数,此时索引失效,使用全表扫描
- SELECTacc_num
- ,curr_cd
- ,DECODE('20110728'
- ,(SELECTTO_CHAR(LAST_DAY(TO_DATE('20110728','YYYYMMDD')),'YYYYMMDD')FROMdual),0
- ,adj_credit_int_lv1_amt
- +adj_credit_int_lv2_amt
- -adj_debit_int_lv1_amt
- -adj_debit_int_lv2_amt)
- ASinterest
- FROMacc_pos_int_tbl
- WHERESUBSTR(business_date,1,6)=SUBSTR('20110728',1,6)ANDbusiness_date<='20110728';
- -->改进的办法
- SELECTacc_num
- ,curr_cd
- ,DECODE('20110728'
- ,(SELECTTO_CHAR(LAST_DAY(TO_DATE('20110728','YYYYMMDD')),'YYYYMMDD')FROMdual),0
- ,adj_credit_int_lv1_amt
- +adj_credit_int_lv2_amt
- -adj_debit_int_lv1_amt
- -adj_debit_int_lv2_amt)
- ASinterest
- FROMacc_pos_int_tblacc_pos_int_tbl
- WHEREbusiness_date>=TO_CHAR(LAST_DAY(ADD_MONTHS(TO_DATE('20110728','yyyymmdd'),-1))
- +1,'yyyymmdd')
- ANDbusiness_date<='20110728';
- -->下面的例子虽然没有使用函数,但字符串连接同样导致索引失效
- -->低效:
- SELECTaccount_name,amount
- FROMtransaction
- WHEREaccount_name
- ||account_type='AMEXA';
- -->高效:
- SELECTaccount_name,amount
- FROMtransaction
- WHEREaccount_name='AMEX'ANDaccount_type='A';
- 5)比较不匹配的数据类型
- -->下面的查询中business_date列上存在索引,且为字符型,这种
- -->低效:
- SELECT*
- FROMacc_pos_int_tbl
- WHEREbusiness_date=20090201;
- ExecutionPlan
- ----------------------------------------------------------
- Planhashvalue:2335235465
- -------------------------------------------------------------------------------------
- |Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|
- -------------------------------------------------------------------------------------
- |0|SELECTSTATEMENT||37516|2857K|106K(1)|00:21:17|
- |*1|TABLEACCESSFULL|ACC_POS_INT_TBL|37516|2857K|106K(1)|00:21:17|
- -------------------------------------------------------------------------------------
- PredicateInformation(identifiedbyoperationid):
- ---------------------------------------------------
- 1-filter(TO_NUMBER("BUSINESS_DATE")=20090201)-->这里可以看到产生了类型转换
- -->高效:
- SELECT*
- FROMacc_pos_int_tbl
- WHEREbusiness_date='20090201'
- 6)索引列上使用NULL值
- ISNULL和ISNOTNULL会限制索引的使用,因为数据中没有值等于NULL值,即便是NULL值也不等于NULL值.且NULL值不存储在于索引之中
- 因此应尽可能避免在索引类上使用NULL值
- SELECTacc_num
- ,pl_cd
- ,order_qty
- ,trade_date
- FROMtrade_client_tbl
- WHEREinput_dateISNOTNULL;
- ExecutionPlan
- ----------------------------------------------------------
- Planhashvalue:901462645
- --------------------------------------------------------------------------------------
- |Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|
- --------------------------------------------------------------------------------------
- |0|SELECTSTATEMENT||1|44|15(0)|00:00:01|
- |*1|TABLEACCESSFULL|TRADE_CLIENT_TBL|1|44|15(0)|00:00:01|
- --------------------------------------------------------------------------------------
- altertabletrade_client_tblmodify(input_datenotnull);
- 不推荐使用的查询方式
- SELECT*FROMtable_nameWHEREcolISNOTNULL
- SELECT*FROMtable_nameWHEREcolISNULL
- 推荐使用的方式
- SELECT*FROMtable_nameWHEREcol>=0--尽可能的使用=,>=,<=,like等运算符
- -->Author:RobinsonCheng
- -->Blog:http://blog.csdn.net/robinson_0612
三、总结
1、尽可能最小化基表数据以及中间结果集(通过过滤条件避免后续产生不必要的计算与聚合)
2、为where子句中的谓词信息提供最佳的访问路径(rowid访问,索引访问)
3、使用合理的SQL写法来避免过多的Oracle内部开销以提高性能
4、合理的使用提示以提高表之间的连接来提高连接效率(如避免迪卡尔集,将不合理的嵌套连接改为hash连接等)
四、更多参考
dbms_xplan之display_cursor函数的使用
相关推荐
此外,在执行SQL语句时,如插入或更新包含中文的字符串,也要注意设置Statement或PreparedStatement的编码。 在处理乱码问题时,一个重要的工具是`Charset`类,它可以用来检测、转换和比较字符集。`CharsetEncoder`...
在.NET框架中,有多种方法实现对象持久性,包括手动编写SQL语句、使用ADO.NET、以及利用ORM(对象关系映射)工具。 描述中提到的“使用敏捷的托管数据库框架”,这可能是指使用.NET框架中的轻量级数据库框架,如...
同步电机无传感HFI高频谐波注入模型及代码解析,PMSM永磁同步电机滑模观测器仿真模型研究,基于28035的典型HFI方案实现,详细解析参数实现过程与机理,工程实践与理论基础相结合的SOP代码应用,基于无传感HFI高频谐波注入模型的PMSM永磁同步电机同步控制技术:解析与代码实现,同步电机无传感HFI高频谐波注入模型+代码 PMSM永磁同步电机无传感器滑模观测器仿真模型(基于28035),典型的HFI方案; 代码为实际应用SOP代码,非一般玩票代码可比(非ti例程);解析说明详细描述了参数实现过程和实现机理,工程实践和理论基础结合。 ,核心关键词:同步电机;无传感HFI高频谐波注入模型;PMSM永磁同步电机;滑模观测器;仿真模型;28035;HFI方案;SOP代码;参数实现过程;实现机理;工程实践;理论基础。,基于HFI高频谐波注入的PMSM无传感器控制模型与SOP代码实现
一个采用thinkphp3.2开发的商城系统,无论从前台模板的设计,还是后台php的开,都具有非常好的学习价值。首先作为一套前台模板框架的学习总结成果。.zip项目工程资源经过严格测试运行并且功能上ok,可实现复现复刻,拿到资料包后可实现复现出一样的项目,本人系统开发经验充足(全栈全领域),有任何使用问题欢迎随时与我联系,我会抽时间努力为您解惑,提供帮助 【资源内容】:包含源码+工程文件+说明等。答辩评审平均分达到96分,放心下载使用!可实现复现;设计报告也可借鉴此项目;该资源内项目代码都经过测试运行;功能ok 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 【提供帮助】:有任何使用上的问题欢迎随时与我联系,抽时间努力解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 下载后请首先打开说明文件(如有);整理时不同项目所包含资源内容不同;项目工程可实现复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用
无人驾驶车辆动力学模型验证与预测控制资料详解:视频教程及carsim2019与matlab2018应用,无人驾驶车辆动力学模型验证与预测控制资料详解:视频教程及carsim2019与matlab2018应用,无人驾驶的车辆动力学模型验证。 配套详细视频讲解。 配套无人驾驶车辆模型预测控制资料,有视频讲解carsim2019,matlab2018 ,无人驾驶车辆动力学模型验证;配套视频讲解;无人驾驶车辆模型预测控制;carsim2019视频讲解;matlab2018资料,无人驾驶车辆动力学模型验证与MPC控制技术详解视频
项目工程资源经过严格测试运行并且功能上ok,可实现复现复刻,拿到资料包后可实现复现出一样的项目,本人系统开发经验充足(全栈全领域),有任何使用问题欢迎随时与我联系,我会抽时间努力为您解惑,提供帮助 【资源内容】:包含源码+工程文件+说明等。答辩评审平均分达到96分,放心下载使用!可实现复现;设计报告也可借鉴此项目;该资源内项目代码都经过测试运行,功能ok 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 【提供帮助】:有任何使用上的问题欢迎随时与我联系,抽时间努力解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 下载后请首先打开说明文件(如有);整理时不同项目所包含资源内容不同;项目工程可实现复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用
**基于多维度仿真的质子交换膜燃料电池模型构建与性能研究**,基于电化学经验模型与车辆参数,质子交换膜燃料电池稳态与动态建模仿真分析及特性研究,质子交膜燃料电池(#PEMFC) 稳态 AND 动态建模及仿真分析 Note:硕士lunwen复Xian;title:质子交膜燃料电池建模仿真与特性研究 内容: 1. 根据车辆结构参数和性能参数 确定燃料电池组相关参数, eg. 额定功率,最大功率等. (根据需求可省略,或改进); 2. 基于电化学经验模型,建立PEMFC 燃料电池的稳态数学模型; 3. 在稳态数学模型的基础上,考虑燃料电池双层电荷层现象以及电池电堆动态热传输的影响,建立PEMFC 电化学动态模型; 4. 建立稳态 AND 动态Simulink仿真模型; 5. 通过Signal Builder 模拟随时间阶跃下降的外加负载信号,Simulink仿真燃料电池的输出电压,输出功率,消耗功率,电池效率的变化曲线, 并详细分析了电池的稳态 动态响应特性以及影响因素; 6. 极其详尽的模型说明书(包含数学建模,simulink建模,模型结果分析,etc.) AND 图
项目工程资源经过严格测试运行并且功能上ok,可实现复现复刻,拿到资料包后可实现复现出一样的项目,本人系统开发经验充足(全栈全领域),有任何使用问题欢迎随时与我联系,我会抽时间努力为您解惑,提供帮助 【资源内容】:包含源码+工程文件+说明等。答辩评审平均分达到96分,放心下载使用!可实现复现;设计报告也可借鉴此项目;该资源内项目代码都经过测试运行,功能ok 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 【提供帮助】:有任何使用上的问题欢迎随时与我联系,抽时间努力解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 下载后请首先打开说明文件(如有);整理时不同项目所包含资源内容不同;项目工程可实现复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用
推荐,YOLOV5目标检测全套视频课程,共7节。 1.任务需求与项目概述.mp4 2-数据与标签配置方法.mp4 3-标签转格式脚本制作.mp4 4-各版本模型介绍.mp4 5-项目参数配置.mp4 6-缺陷检测模型培训.mp4 7-输出结果与项目总结.mp4
智慧农业解决方案 -促进产业结构转型,突破传统业态.ppt
西门子200smart与昆仑通态锅炉换热站智能控制程序实例:涵盖模拟量读取、自动切换控制、时间段加热与温度设定、电能监控及Modbus通讯变频器控制。,西门子200smart与昆仑通态锅炉换热站程序实例:模拟量读取、自动切换与时间加热控制,Modbus通讯变频器电能管理解决方案,西门子200smart和昆仑通态锅炉热站程序项目实例,程序内有利用模拟量读取,运算时间自动切,水泵一用一备故障自动切,自动时间段加热,时间段设定温度,电能读取及算法 modbus通讯控制变频器。 ,核心关键词: 西门子200smart; 昆仑通态锅炉; 换热站程序项目; 模拟量读取; 运算时间自动切换; 水泵一用一备故障自动切换; 自动时间段加热; 设定温度; 电能读取; 算法; Modbus通讯; 控制变频器。,西门子Smart200程序控制锅炉换热站:智能换热与节能优化管理实例
项目工程资源经过严格测试运行并且功能上ok,可实现复现复刻,拿到资料包后可实现复现出一样的项目,本人系统开发经验充足(全栈全领域),有任何使用问题欢迎随时与我联系,我会抽时间努力为您解惑,提供帮助 【资源内容】:包含源码+工程文件+说明等。答辩评审平均分达到96分,放心下载使用!可实现复现;设计报告也可借鉴此项目;该资源内项目代码都经过测试运行,功能ok 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 【提供帮助】:有任何使用上的问题欢迎随时与我联系,抽时间努力解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 下载后请首先打开说明文件(如有);整理时不同项目所包含资源内容不同;项目工程可实现复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用
相移格雷码解相位程序开发与条纹结构光编解码技术应用于单双目结构光三维重建系统,相移格雷码解相位程序开发:条纹结构光编解码技术助力单目双目结构光三维重建系统,相移格雷码解相位程序开发 条纹结构光编解码,可用于单目或双目结构光三维重建系统 ,相移格雷码解相位程序开发; 结构光编解码; 单目结构光; 双目结构光; 三维重建系统,相移格雷码解相位程序开发:单目双目结构光三维重建系统编解码技术
高集成伺服电机驱控芯片TMC9660例子代码
项目工程资源经过严格测试运行并且功能上ok,可实现复现复刻,拿到资料包后可实现复现出一样的项目,本人系统开发经验充足(全栈全领域),有任何使用问题欢迎随时与我联系,我会抽时间努力为您解惑,提供帮助 【资源内容】:包含源码+工程文件+说明等。答辩评审平均分达到96分,放心下载使用!可实现复现;设计报告也可借鉴此项目;该资源内项目代码都经过测试运行,功能ok 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 【提供帮助】:有任何使用上的问题欢迎随时与我联系,抽时间努力解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 下载后请首先打开说明文件(如有);整理时不同项目所包含资源内容不同;项目工程可实现复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用
基于任务链的中小工厂数字化新路径 高效搭建有竞争力的数字工厂.pptx
光伏并网逆变器设计方案与高效实现:结合matlab电路仿真、DSP代码及环流抑制策略,光伏并网逆变器设计方案:结合matlab电路文件与DSP程序代码,实现高效并联环流抑制策略,光伏并网逆变器设计方案,附有相关的matlab电路文件,以及DSP的程序代码,方案、仿真文件、代码三者结合使用效果好,事半功倍。 备注:赠送逆变器并联环流matlab文件,基于矢量控制的环流抑制策略和下垂控制的环流抑制 ,光伏并网逆变器设计方案; MATLAB电路文件; DSP程序代码; 方案、仿真文件、代码结合使用; 并联环流抑制策略; 下垂控制的环流抑制,光伏并网逆变器优化设计:方案、仿真与DSP程序代码三合一,并赠送并联环流抑制策略Matlab文件
项目工程资源经过严格测试运行并且功能上ok,可实现复现复刻,拿到资料包后可实现复现出一样的项目,本人系统开发经验充足(全栈全领域),有任何使用问题欢迎随时与我联系,我会抽时间努力为您解惑,提供帮助 【资源内容】:包含源码+工程文件+说明等。答辩评审平均分达到96分,放心下载使用!可实现复现;设计报告也可借鉴此项目;该资源内项目代码都经过测试运行;功能ok 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 【提供帮助】:有任何使用上的问题欢迎随时与我联系,抽时间努力解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 下载后请首先打开说明文件(如有);整理时不同项目所包含资源内容不同;项目工程可实现复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用
校园在线拍卖系统(源码+数据库+论文+ppt)java开发springboot框架javaweb,可做计算机毕业设计或课程设计 【功能需求】 按照校园在线拍卖系统的角色,我划分为了用户模块(拍卖者和用户)和管理员模块这两大部分。 拍卖者模块: (1)注册登录:用户注册为会员并登录校园在线拍卖系统;用户对个人信息的增删改查,比如个人资料,密码修改。 (2)竞拍商品管理:拍卖者可以对竞拍商品进行增删改查。 (3)竞拍订单管理:拍卖者可以看到用户提交的竞拍价格信息以及产品,可以对竞拍订单进行审核。 (4)评价订单管理:可以在此页面查看到用户提交的订单评价信息等。 (5)在线留言:可以回复用户的在线留言信息。 用户模块: (1)用户注册登录:用户注册为会员并登录校园在线拍卖系统;用户对个人信息的增删改查,比如个人资料,密码修改。 (2)拍卖资讯:用户可以在此模块中浏览系统发布的最新拍卖资讯。 (3)竞拍商品:用户可以查看到竞拍商品详情。 (4)在线竞拍:用户可以在竞拍商品下方点击立即竞拍,提交竞拍信息。 (5)在线留言:用户可以提交在线留言信息。 (6)竞拍订单:可以在线进行竞拍商品订单的支付。 (7)评价订单:支付后可以对订单进行评价。 管理员管理模块: (1)用户管理:管理员可以对前台上注册过的用户信息进行管控,对拍卖者信息进行审核,也可以对管理员信息进行管控。 (2)用户管理:管理员对系统用户的管理。 (3)商品分类管理:对商品进行分类管理。 (4)竞拍商品管理:对拍卖者发布的拍卖商品进行管理。 (5)竞拍订单管理:对用户提交的竞拍订单信息进行管理。 (6)评价订单管理:对用户的评价信息进行管理。 (7)在线留言管理:对用户的留言信息进行管理。 (8)系统管理:对通知公告、竞拍资讯、轮播图管理。 【环境需要】 1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。 2.IDE环境:IDEA,Eclipse,Myeclipse都可以。 3.tomcat环境:Tomcat 7.x,8.x,9.x版本均可 4.数据库:MySql 5.7/8.0等版本均可; 【购买须知】 本源码项目经过严格的调试,项目已确保无误,可直接用于课程实训或毕业设计提交。里面都有配套的运行环境软件,讲解视频,部署视频教程,一应俱全,可以自己按照教程导入运行。附有论文参考,使学习者能够快速掌握系统设计和实现的核心技术。