`
yuxuan1215
  • 浏览: 14733 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

高效SQL语句必杀技

 
阅读更多

No SQL,No cost. SQL语句是造成数据库开销最大的部分。而不良SQL写法直接导致数据库系统性能下降的情形比比皆是。那么如何才能称得
上高效的SQL语句呢?一是查询优化器为当前的SQL语句生成最佳的执行计划,保证数据读写使用最佳路径;二是设置合理的物理存储结构,如表
的类型,字段的顺序,字段的数据类型等。本文主要描述如何编写高效的SQL语句并给出示例。下面的描述主要分为三个部分,一是编写高效SQL
语句,二是使用索引提高查询性能的部分,三是总结部分。

一、编写高效SQL语句

  1. 1)选择最有效的表名顺序(仅适用于RBO模式)
  2. ORACLE的解析器总是按照从右到左的顺序处理FROM子句中的表名,因此FROM子句中最后的一个表将作为驱动表被优先处理。当FROM子句
  3. 存在多个表的时候,应当考虑将表上记录最少的那个表置于FROM的最右端作为基表。Oracle会首先扫描基表(FROM子句中最后的那个表)并对
  4. 记录进行排序,然后扫描第二个表(FROM子句中最后第二个表),最后将所有从第二个表中检索出的记录与第一个表中合适记录进行合并。如
  5. 果有3个以上的表连接查询,那就需要选择交叉表(intersectiontable)作为基础表,交叉表是指那个被其他表所引用的表。
  6. 下面的例子使用最常见的scott或hr模式下的表进行演示
  7. 表EMP有14条记录
  8. 表DEPT有4条记录
  9. SELECT/*+rule*/COUNT(*)FROMemp,dept;--高效的写法
  10. scott@CNMMBO>setautotracetraceonlystat;
  11. scott@CNMMBO>SELECT/*+rule*/COUNT(*)FROMemp,dept;
  12. Elapsed:00:00:00.14
  13. Statistics
  14. ----------------------------------------------------------
  15. 1recursivecalls
  16. 0dbblockgets
  17. 35consistentgets
  18. 0physicalreads
  19. 0redosize
  20. 515bytessentviaSQL*Nettoclient
  21. 492bytesreceivedviaSQL*Netfromclient
  22. 2SQL*Netroundtripsto/fromclient
  23. 0sorts(memory)
  24. 0sorts(disk)
  25. 1rowsprocessed
  26. SELECT/*+rule*/COUNT(*)FROMdept,emp;--低效的写法
  27. scott@CNMMBO>SELECT/*+rule*/COUNT(*)FROMdept,emp;
  28. Elapsed:00:00:00.02
  29. Statistics
  30. ----------------------------------------------------------
  31. 1recursivecalls
  32. 0dbblockgets
  33. 105consistentgets
  34. 0physicalreads
  35. 0redosize
  36. 515bytessentviaSQL*Nettoclient
  37. 492bytesreceivedviaSQL*Netfromclient
  38. 2SQL*Netroundtripsto/fromclient
  39. 0sorts(memory)
  40. 0sorts(disk)
  41. 1rowsprocessed
  42. 2)select查询中避免使用'*'
  43. 当你想在SELECT子句中列出所有的COLUMN时,使用动态SQL列引用'*'是一个方便的方法.不幸的是,这是一个非常低效的方法.实际
  44. 上,ORACLE在解析的过程中,会将'*'依次转换成所有的列名,这个工作是通过查询数据字典完成的,这意味着将耗费更多的时间。
  45. 注:本文中的例子出于简化演示而使用了select*,生产环境应避免使用.
  46. 3)减少访问数据库的次数
  47. 每当执行一条SQL语句,Oracle需要完成大量的内部操作,象解析SQL语句,估算索引的利用率,绑定变量,读数据块等等.由此可
  48. 见,减少访问数据库的次数,实际上是降低了数据库系统开销
  49. -->下面通过3种方式来获得雇员编号为7788与7902的相关信息
  50. -->方式1(最低效):
  51. selectename,job,salfromempwhereempno=7788;
  52. selectename,job,salfromempwhereempno=7902;
  53. -->方式2(次低效):
  54. -->下面使用了参数游标来完成,每传递一次参数则需要对表emp访问一次,增加了I/O
  55. DECLARE
  56. CURSORC1(E_NONUMBER)IS
  57. SELECTename,job,sal
  58. FROMemp
  59. WHEREempno=E_NO;
  60. BEGIN
  61. OPENC1(7788);
  62. FETCHC1INTO…,…,…;
  63. ..
  64. OPENC1(7902);
  65. FETCHC1INTO…,…,…;
  66. CLOSEC1;
  67. END;
  68. -->方式3(最高效)
  69. SELECTa.ename
  70. ,a.job
  71. ,a.sal
  72. ,b.ename
  73. ,b.job
  74. ,b.sal
  75. FROMempa,empb
  76. WHEREa.empno=7788ORb.empno=7902;
  77. 注意:在SQL*Plus,SQL*Forms和Pro*C中重新设置ARRAYSIZE参数,可以增加每次数据库访问的检索数据量,建议值为200.
  78. 4)使用DECODE函数来减少处理时间
  79. -->使用decode函数可以避免重复扫描相同的行或重复连接相同的表
  80. selectcount(*),sum(sal)fromempwheredeptno=20andenamelike'SMITH%';
  81. selectcount(*),sum(sal)fromempwheredeptno=30andenamelike'SMITH%';
  82. -->通过使用decode函数一次扫描即可完成所有满足条件记录的处理
  83. SELECTCOUNT(DECODE(deptno,20,'x',NULL))d20_count
  84. ,COUNT(DECODE(deptno,30,'x',NULL))d30_count
  85. ,SUM(DECODE(deptno,20,sal,NULL))d20_sal
  86. ,SUM(DECODE(deptno,30,sal,NULL))d30_sal
  87. FROMemp
  88. WHEREenameLIKE'SMITH%';
  89. 类似的,DECODE函数也可以运用于GROUPBYORDERBY子句中。
  90. 5)整合简单,无关联的数据库访问
  91. -->如果你有几个简单的数据库查询语句,你可以把它们整合到一个查询中以提高性能(即使它们之间没有关系)
  92. -->整合前
  93. SELECTname
  94. FROMemp
  95. WHEREempno=1234;
  96. SELECTname
  97. FROMdept
  98. WHEREdeptno=10;
  99. SELECTname
  100. FROMcat
  101. WHEREcat_type='RD';
  102. -->整合后
  103. SELECTe.name,d.name,c.name
  104. FROMcatc
  105. ,dptd
  106. ,empe
  107. ,dualx
  108. WHERENVL('X',x.dummy)=NVL('X',e.ROWID(+))
  109. ANDNVL('X',x.dummy)=NVL('X',d.ROWID(+))
  110. ANDNVL('X',x.dummy)=NVL('X',c.ROWID(+))
  111. ANDe.emp_no(+)=1234
  112. ANDd.dept_no(+)=10
  113. ANDc.cat_type(+)='RD';
  114. -->从上面的SQL语句可以看出,尽管三条语句被整合为一条,性能得以提高,然可读性差,此时应权衡性能与代价
  115. 6)删除重复记录
  116. -->通过使用rowid来作为过滤条件,性能高效
  117. DELETEFROMempe
  118. WHEREe.ROWID>(SELECTMIN(x.ROWID)
  119. FROMempx
  120. WHEREx.empno=e.empno);
  121. 7)使用truncate代替delete
  122. -->通常情况下,任意记录的删除需要在回滚段构造删除前镜像以实现回滚(rollback).对于未提交的数据在执行rollback之后,Oracle会生成
  123. -->等价SQL语句去恢复记录(如delete,则生成对应的insert语句;如insert则生成对应的delete;如update,则是同时生成delete和insert
  124. -->使用truncate命令则是执行DDL命令,不产生任何回滚信息,直接格式化并释放高水位线.故该语句性能高效.由于不能rollback,因此慎用.
  125. 8)尽量多使用COMMIT(COMMIT应确保事务的完整性)
  126. -->只要有可能,在程序中尽量多使用COMMIT,这样程序的性能得到提高,需求也会因为COMMIT所释放的资源而减少
  127. -->COMMIT所释放的资源:
  128. -->1.回滚段上用于恢复数据的信息
  129. -->2.释放语句处理期间所持有的锁
  130. -->3.释放redologbuffer占用的空间(commit将redologbuffer中的entries写入到联机重做日志文件)
  131. -->4.ORACLE为管理上述3种资源中的内部开销
  132. 9)计算记录条数
  133. -->一般的情况下,count(*)比count(1)稍快.如果可以通过索引检索,对索引列的计数是最快的,因为直接扫描索引即可,例如COUNT(EMPNO)
  134. -->实际情况是经测试上述三种情况并无明显差异.
  135. 10)用Where子句替换HAVING子句
  136. -->尽可能的避免having子句,因为HAVING子句是对检索出所有记录之后再对结果集进行过滤。这个处理需要排序,总计等操作
  137. -->通过WHERE子句则在分组之前即可过滤不必要的记录数目,从而减少聚合的开销
  138. -->低效:
  139. SELECTdeptno,AVG(sal)
  140. FROMemp
  141. GROUPBYdeptno
  142. HAVINGdeptno=20;
  143. scott@CNMMBO>SELECTdeptno,AVG(sal)
  144. 2FROMemp
  145. 3GROUPBYdeptno
  146. 4HAVINGdeptno=20;
  147. Statistics
  148. ----------------------------------------------------------
  149. 0recursivecalls
  150. 0dbblockgets
  151. 7consistentgets
  152. 0physicalreads
  153. 0redosize
  154. 583bytessentviaSQL*Nettoclient
  155. 492bytesreceivedviaSQL*Netfromclient
  156. 2SQL*Netroundtripsto/fromclient
  157. 0sorts(memory)
  158. 0sorts(disk)
  159. 1rowsprocessed
  160. -->高效:
  161. SELECTdeptno,AVG(sal)
  162. FROMemp
  163. WHEREdeptno=20
  164. GROUPBYdeptno;
  165. scott@CNMMBO>SELECTdeptno,AVG(sal)
  166. 2FROMemp
  167. 3WHEREdeptno=20
  168. 4GROUPBYdeptno;
  169. Statistics
  170. ----------------------------------------------------------
  171. 0recursivecalls
  172. 0dbblockgets
  173. 2consistentgets
  174. 0physicalreads
  175. 0redosize
  176. 583bytessentviaSQL*Nettoclient
  177. 492bytesreceivedviaSQL*Netfromclient
  178. 2SQL*Netroundtripsto/fromclient
  179. 0sorts(memory)
  180. 0sorts(disk)
  181. 1rowsprocessed
  182. 11)最小化表查询次数
  183. -->在含有子查询的SQL语句中,要特别注意减少对表的查询
  184. -->低效:
  185. SELECT*
  186. FROMemployees
  187. WHEREdepartment_id=(SELECTdepartment_id
  188. FROMdepartments
  189. WHEREdepartment_name='Marketing')
  190. ANDmanager_id=(SELECTmanager_id
  191. FROMdepartments
  192. WHEREdepartment_name='Marketing');
  193. -->高效:
  194. SELECT*
  195. FROMemployees
  196. WHERE(department_id,manager_id)=(SELECTdepartment_id,manager_id
  197. FROMdepartments
  198. WHEREdepartment_name='Marketing')
  199. -->类似更新多列的情形
  200. -->低效:
  201. UPDATEemployees
  202. SETjob_id=(SELECTMAX(job_id)FROMjobs),salary=(SELECTAVG(min_salary)FROMjobs)
  203. WHEREdepartment_id=10;
  204. -->高效:
  205. UPDATEemployees
  206. SET(job_id,salary)=(SELECTMAX(job_id),AVG(min_salary)FROMjobs)
  207. WHEREdepartment_id=10;
  208. 12)使用表别名
  209. -->在多表查询时,为所返回列使用表别名作为前缀以减少解析时间以及那些相同列歧义引起的语法错误
  210. 13)用EXISTS替代IN
  211. 在一些基于基础表的查询中,为了满足一个条件,往往需要对另一个表进行联接.在这种情况下,使用EXISTS(或NOTEXISTS)通常
  212. 将提高查询的效率.
  213. -->低效:
  214. SELECT*
  215. FROMemp
  216. WHEREsal>1000
  217. ANDdeptnoIN(SELECTdeptno
  218. FROMdept
  219. WHEREloc='DALLAS')
  220. -->高效:
  221. SELECT*
  222. FROMemp
  223. WHEREempno>1000
  224. ANDEXISTS
  225. (SELECT1
  226. FROMdept
  227. WHEREdeptno=emp.deptnoANDloc='DALLAS')
  228. 14)用NOTEXISTS替代NOTIN
  229. 在子查询中,NOTIN子句引起一个内部的排序与合并.因此,无论何时NOTIN子句都是最低效的,因为它对子查询中的表执行了一个全表
  230. 遍历.为避免该情形,应当将其改写成外部连接(OUTTERJOIN)或适用NOTEXISTS
  231. -->低效:
  232. SELECT*
  233. FROMemp
  234. WHEREdeptnoNOTIN(SELECTdeptno
  235. FROMdept
  236. WHEREloc='DALLAS');
  237. -->高效:
  238. SELECTe.*
  239. FROMempe
  240. WHERENOTEXISTS
  241. (SELECT1
  242. FROMdept
  243. WHEREdeptno=e.deptnoANDloc='DALLAS');
  244. -->最高效(尽管下面的查询最高效,并不推荐使用,因为列loc使用了不等运算,当表dept数据量较大,且loc列存在索引的话,则此时索引失效)
  245. SELECTe.*
  246. FROMempeLEFTJOINdeptdONe.deptno=d.deptno
  247. WHEREd.loc<>'DALLAS'
  248. 15)使用表连接替换EXISTS
  249. 一般情况下,使用表连接比EXISTS更高效
  250. -->低效:
  251. SELECT*
  252. FROMemployeese
  253. WHEREEXISTS
  254. (SELECT1
  255. FROMdepartments
  256. WHEREdepartment_id=e.department_idANDdepartment_name='IT');
  257. -->高效:
  258. SELECT*-->经测试此写法SQLplus下比上面的写法多一次逻辑读,而在Toad下两者结果一致
  259. FROMemployeeseINNERJOINdepartmentsdONd.department_id=e.department_id
  260. WHEREd.department_name='IT';
  261. 16)用EXISTS替换DISTINCT
  262. 对于一对多关系表信息查询时(如部门表和雇员表),应避免在select子句中使用distinct,而使用exists来替换
  263. -->低效:
  264. SELECTDISTINCTe.department_id,d.department_name
  265. FROMdepartmentsdINNERJOINemployeeseONd.department_id=e.department_id;
  266. -->高效:
  267. SELECTd.department_id,department_name
  268. fromdepartmentsd
  269. WHEREEXISTS
  270. (SELECT1
  271. FROMemployeese
  272. WHEREd.department_id=e.department_id);
  273. EXISTS使查询更为迅速,因为RDBMS核心模块将在子查询的条件一旦满足后,立刻返回结果
  274. -->经测试此写法SQLplus下比上面的写法多一次逻辑读,而在Toad下两者结果一致
  275. 17)使用UNIONALL替换UNION(如果有可能的话)
  276. 当SQL语句需要UNION两个查询结果集时,这两个结果集合会以UNION-ALL的方式被合并,然后在输出最终结果前进行排序。
  277. 如果用UNIONALL替代UNION,这样排序就不是必要了。效率就会因此得到提高。
  278. 注意:
  279. UNIONALL会输出所有的结果集,而UNION则过滤掉重复记录并对其进行排序.因此在使用时应考虑业务逻辑是否允许当前的结果集存在重复现象
  280. 寻找低效的SQL语句
  281. -->下面的语句主要适用于从视图v$sqlarea中获得当前运行下且耗用buffer_gets较多的SQL语句
  282. SELECTexecutions
  283. ,disk_reads
  284. ,buffer_gets
  285. ,ROUND((buffer_gets
  286. -disk_reads)
  287. /buffer_gets,2)
  288. hit_ratio
  289. ,ROUND(disk_reads/executions,2)reads_per_run
  290. ,sql_text
  291. FROMv$sqlarea
  292. WHEREexecutions>0
  293. ANDbuffer_gets>0
  294. AND(buffer_gets
  295. -disk_reads)
  296. /buffer_gets<0.80
  297. ORDERBY4DESC;
  298. 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. 1)避免基于索引列的计算
  2. where子句中的谓词上存在索引,而此时基于该列的计算将使得索引失效
  3. -->低效:
  4. SELECTemployee_id,first_name
  5. FROMemployees
  6. WHEREemployee_id+10>150;-->索引列上使用了计算,因此索引失效,走全表扫描方式
  7. -->高效:
  8. SELECTemployee_id,first_name
  9. FROMemployees
  10. WHEREemployee_id>160;-->走索引范围扫描方式
  11. 例外情形
  12. 上述规则不适用于SQL中的MINMAX函数
  13. hr@CNMMBO>SELECTMAX(employee_id)max_id
  14. 2FROMemployees
  15. 3WHEREemployee_id
  16. 4+10>150;
  17. 1rowselected.
  18. ExecutionPlan
  19. ----------------------------------------------------------
  20. Planhashvalue:1481384439
  21. ---------------------------------------------------------------------------------------------
  22. |Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|
  23. ---------------------------------------------------------------------------------------------
  24. |0|SELECTSTATEMENT||1|4|1(0)|00:00:01|
  25. |1|SORTAGGREGATE||1|4|||
  26. |2|FIRSTROW||5|20|1(0)|00:00:01|
  27. |*3|INDEXFULLSCAN(MIN/MAX)|EMP_EMP_ID_PK|5|20|1(0)|00:00:01|
  28. ---------------------------------------------------------------------------------------------
  29. 2)避免在索引列上使用NOT运算或不等于运算(<>,!=)
  30. 通常,我们要避免在索引列上使用NOT或<>,两者会产生在和在索引列上使用函数相同的影响。当ORACLE遇到NOT或不等运算时,他就会停止
  31. 使用索引转而执行全表扫描。
  32. -->低效:
  33. SELECT*
  34. FROMemp
  35. WHERENOT(deptno=20);-->实际上NOT(deptno=20)等同于deptno<>20,即deptno<>同样会限制索引
  36. -->高效:
  37. SELECT*
  38. FROMemp
  39. WHEREdeptno>20ORdeptno<20;
  40. -->尽管此方式可以替换且实现上述结果,但依然走全表扫描,如果是单纯的>或<运算,则此时为索引范围扫描
  41. 需要注意的是,在某些时候,ORACLE优化器会自动将NOT转化成相对应的关系操作符
  42. 其次如果是下列运算符进行NOT运算,依然有可能选择走索引,仅仅除了NOT=之外,因为NOT=等价于<>
  43. NOT>”to<=
  44. NOT>=”to<
  45. NOT<”to>=
  46. NOT<=”to>
  47. 来看一个实际的例子
  48. hr@CNMMBO>SELECT*
  49. 2FROMemployees
  50. 3wherenotemployee_id<100;-->索引列上使用了not,但是该查询返回了所有的记录,即107条,因此此时选择走全表扫描
  51. 107rowsselected.
  52. ExecutionPlan
  53. ----------------------------------------------------------
  54. Planhashvalue:1445457117
  55. -------------------------------------------------------------------------------
  56. |Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|
  57. -------------------------------------------------------------------------------
  58. |0|SELECTSTATEMENT||107|7276|3(0)|00:00:01|
  59. |*1|TABLEACCESSFULL|EMPLOYEES|107|7276|3(0)|00:00:01|-->执行计划中使用了走全表扫描方式
  60. -------------------------------------------------------------------------------
  61. PredicateInformation(identifiedbyoperationid):
  62. ---------------------------------------------------
  63. 1-filter("EMPLOYEE_ID">=100)-->查看这里的谓词信息被自动转换为>=运算符
  64. hr@CNMMBO>SELECT*
  65. 2FROMemployees
  66. 3wherenotemployee_id<140;-->此例与上面的语句相同,仅仅是查询范围不同返回67条记录,而此时选择了索引范围扫描
  67. 67rowsselected.
  68. ExecutionPlan
  69. ----------------------------------------------------------
  70. Planhashvalue:603312277
  71. ---------------------------------------------------------------------------------------------
  72. |Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|
  73. ---------------------------------------------------------------------------------------------
  74. |0|SELECTSTATEMENT||68|4624|3(0)|00:00:01|
  75. |1|TABLEACCESSBYINDEXROWID|EMPLOYEES|68|4624|3(0)|00:00:01|
  76. |*2|INDEXRANGESCAN|EMP_EMP_ID_PK|68||1(0)|00:00:01|-->索引范围扫描方式
  77. ---------------------------------------------------------------------------------------------
  78. PredicateInformation(identifiedbyoperationid):
  79. ---------------------------------------------------
  80. 2-access("EMPLOYEE_ID">=140)
  81. 3)用UNION替换OR(适用于索引列)
  82. 通常情况下,使用UNION替换WHERE子句中的OR将会起到较好的效果.基于索引列使用OR使得优化器倾向于使用全表扫描,而不是扫描索引.
  83. 注意,以上规则仅适用于多个索引列有效。如果有column没有被索引,查询效率可能会因为你没有选择OR而降低。
  84. -->低效:
  85. SELECTdeptno,dname
  86. FROMdept
  87. WHEREloc='DALLAS'ORdeptno=20;
  88. -->高效:
  89. SELECTdeptno,dname
  90. FROMdept
  91. WHEREloc='DALLAS'
  92. UNION
  93. SELECTdeptno,dname
  94. FROMdept
  95. WHEREdeptno=30
  96. -->经测试,由于数据量较少,此时where子句中的谓词上都存在索引列时,两者性能相当.
  97. -->假定where子句中存在两列
  98. scott@CNMMBO>createtablet6asselectobject_id,owner,object_namefromdba_objectswhereowner='SYS'andrownum<1001;
  99. scott@CNMMBO>insertintot6selectobject_id,owner,object_namefromdba_objectswhereowner='SCOTT'andrownum<6;
  100. scott@CNMMBO>createindexi_t6_object_idont6(object_id);
  101. scott@CNMMBO>createindexi_t6_owneront6(owner);
  102. scott@CNMMBO>insertintot6selectobject_id,owner,object_namefromdba_objectswhereowner='SYSTEM'andrownum<=300;
  103. scott@CNMMBO>commit;
  104. scott@CNMMBO>execdbms_stats.gather_table_stats('SCOTT','T6',cascade=>true);
  105. scott@CNMMBO>selectowner,count(*)fromt6groupbyowner;
  106. OWNERCOUNT(*)
  107. ------------------------------
  108. SCOTT5
  109. SYSTEM300
  110. SYS1000
  111. scott@CNMMBO>select*fromt6whereowner='SCOTT'andrownum<2;
  112. OBJECT_IDOWNEROBJECT_NAME
  113. --------------------------------------------------
  114. 69450SCOTTT_TEST
  115. scott@CNMMBO>select*fromt6whereobject_id=69450orowner='SYSTEM';
  116. 301rowsselected.
  117. ExecutionPlan
  118. ----------------------------------------------------------
  119. Planhashvalue:238853296
  120. -----------------------------------------------------------------------------------------------
  121. |Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|
  122. -----------------------------------------------------------------------------------------------
  123. |0|SELECTSTATEMENT||300|7200|5(0)|00:00:01|
  124. |1|CONCATENATION||||||
  125. |2|TABLEACCESSBYINDEXROWID|T6|1|24|2(0)|00:00:01|
  126. |*3|INDEXRANGESCAN|I_T6_OBJECT_ID|1||1(0)|00:00:01|
  127. |*4|TABLEACCESSBYINDEXROWID|T6|299|7176|3(0)|00:00:01|
  128. |*5|INDEXRANGESCAN|I_T6_OWNER|300||1(0)|00:00:01|
  129. -----------------------------------------------------------------------------------------------
  130. PredicateInformation(identifiedbyoperationid):
  131. ---------------------------------------------------
  132. 3-access("OBJECT_ID"=69450)
  133. 4-filter(LNNVL("OBJECT_ID"=69450))
  134. 5-access("OWNER"='SYSTEM')
  135. Statistics
  136. ----------------------------------------------------------
  137. 0recursivecalls
  138. 0dbblockgets
  139. 46consistentgets
  140. 0physicalreads
  141. 0redosize
  142. 11383bytessentviaSQL*Nettoclient
  143. 712bytesreceivedviaSQL*Netfromclient
  144. 22SQL*Netroundtripsto/fromclient
  145. 0sorts(memory)
  146. 0sorts(disk)
  147. 301rowsprocessed
  148. scott@CNMMBO>select*fromt6whereowner='SYSTEM'orobject_id=69450;
  149. 301rowsselected.
  150. ExecutionPlan
  151. ----------------------------------------------------------
  152. Planhashvalue:238853296
  153. -----------------------------------------------------------------------------------------------
  154. |Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|
  155. -----------------------------------------------------------------------------------------------
  156. |0|SELECTSTATEMENT||300|7200|5(0)|00:00:01|
  157. |1|CONCATENATION||||||
  158. |2|TABLEACCESSBYINDEXROWID|T6|1|24|2(0)|00:00:01|
  159. |*3|INDEXRANGESCAN|I_T6_OBJECT_ID|1||1(0)|00:00:01|
  160. |*4|TABLEACCESSBYINDEXROWID|T6|299|7176|3(0)|00:00:01|
  161. |*5|INDEXRANGESCAN|I_T6_OWNER|300||1(0)|00:00:01|
  162. -----------------------------------------------------------------------------------------------
  163. PredicateInformation(identifiedbyoperationid):
  164. ---------------------------------------------------
  165. 3-access("OBJECT_ID"=69450)
  166. 4-filter(LNNVL("OBJECT_ID"=69450))
  167. 5-access("OWNER"='SYSTEM')
  168. Statistics
  169. ----------------------------------------------------------
  170. 1recursivecalls
  171. 0dbblockgets
  172. 46consistentgets
  173. 0physicalreads
  174. 0redosize
  175. 11383bytessentviaSQL*Nettoclient
  176. 712bytesreceivedviaSQL*Netfromclient
  177. 22SQL*Netroundtripsto/fromclient
  178. 0sorts(memory)
  179. 0sorts(disk)
  180. 301rowsprocessed
  181. scott@CNMMBO>select*fromt6
  182. 2whereobject_id=69450
  183. 3union
  184. 4select*fromt6
  185. 5whereowner='SYSTEM';
  186. 301rowsselected.
  187. ExecutionPlan
  188. ----------------------------------------------------------
  189. Planhashvalue:370530636
  190. ------------------------------------------------------------------------------------------------
  191. |Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|
  192. ------------------------------------------------------------------------------------------------
  193. |0|SELECTSTATEMENT||301|7224|7(72)|00:00:01|
  194. |1|SORTUNIQUE||301|7224|7(72)|00:00:01|
  195. |2|UNION-ALL||||||
  196. |3|TABLEACCESSBYINDEXROWID|T6|1|24|2(0)|00:00:01|
  197. |*4|INDEXRANGESCAN|I_T6_OBJECT_ID|1||1(0)|00:00:01|
  198. |5|TABLEACCESSBYINDEXROWID|T6|300|7200|3(0)|00:00:01|
  199. |*6|INDEXRANGESCAN|I_T6_OWNER|300||1(0)|00:00:01|
  200. ------------------------------------------------------------------------------------------------
  201. PredicateInformation(identifiedbyoperationid):
  202. ---------------------------------------------------
  203. 4-access("OBJECT_ID"=69450)
  204. 6-access("OWNER"='SYSTEM')
  205. Statistics
  206. ----------------------------------------------------------
  207. 1recursivecalls
  208. 0dbblockgets
  209. 7consistentgets
  210. 0physicalreads
  211. 0redosize
  212. 11383bytessentviaSQL*Nettoclient
  213. 712bytesreceivedviaSQL*Netfromclient
  214. 22SQL*Netroundtripsto/fromclient
  215. 1sorts(memory)
  216. 0sorts(disk)
  217. 301rowsprocessed
  218. -->从上面的统计信息可知,consistentgets由46下降为7,故当where子句中谓词上存在索引时,使用union替换or更高效
  219. -->即使当列object_id与owner上不存在索引时,使用union仍然比or更高效(在Oracle10gR2与Oracle11gR2测试)
  220. 4)避免索引列上使用函数
  221. -->下面是一个来自实际生产环境的例子
  222. -->表acc_pos_int_tbl上business_date列存在索引,由于使用了SUBSTR函数,此时索引失效,使用全表扫描
  223. SELECTacc_num
  224. ,curr_cd
  225. ,DECODE('20110728'
  226. ,(SELECTTO_CHAR(LAST_DAY(TO_DATE('20110728','YYYYMMDD')),'YYYYMMDD')FROMdual),0
  227. ,adj_credit_int_lv1_amt
  228. +adj_credit_int_lv2_amt
  229. -adj_debit_int_lv1_amt
  230. -adj_debit_int_lv2_amt)
  231. ASinterest
  232. FROMacc_pos_int_tbl
  233. WHERESUBSTR(business_date,1,6)=SUBSTR('20110728',1,6)ANDbusiness_date<='20110728';
  234. -->改进的办法
  235. SELECTacc_num
  236. ,curr_cd
  237. ,DECODE('20110728'
  238. ,(SELECTTO_CHAR(LAST_DAY(TO_DATE('20110728','YYYYMMDD')),'YYYYMMDD')FROMdual),0
  239. ,adj_credit_int_lv1_amt
  240. +adj_credit_int_lv2_amt
  241. -adj_debit_int_lv1_amt
  242. -adj_debit_int_lv2_amt)
  243. ASinterest
  244. FROMacc_pos_int_tblacc_pos_int_tbl
  245. WHEREbusiness_date>=TO_CHAR(LAST_DAY(ADD_MONTHS(TO_DATE('20110728','yyyymmdd'),-1))
  246. +1,'yyyymmdd')
  247. ANDbusiness_date<='20110728';
  248. -->下面的例子虽然没有使用函数,但字符串连接同样导致索引失效
  249. -->低效:
  250. SELECTaccount_name,amount
  251. FROMtransaction
  252. WHEREaccount_name
  253. ||account_type='AMEXA';
  254. -->高效:
  255. SELECTaccount_name,amount
  256. FROMtransaction
  257. WHEREaccount_name='AMEX'ANDaccount_type='A';
  258. 5)比较不匹配的数据类型
  259. -->下面的查询中business_date列上存在索引,且为字符型,这种
  260. -->低效:
  261. SELECT*
  262. FROMacc_pos_int_tbl
  263. WHEREbusiness_date=20090201;
  264. ExecutionPlan
  265. ----------------------------------------------------------
  266. Planhashvalue:2335235465
  267. -------------------------------------------------------------------------------------
  268. |Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|
  269. -------------------------------------------------------------------------------------
  270. |0|SELECTSTATEMENT||37516|2857K|106K(1)|00:21:17|
  271. |*1|TABLEACCESSFULL|ACC_POS_INT_TBL|37516|2857K|106K(1)|00:21:17|
  272. -------------------------------------------------------------------------------------
  273. PredicateInformation(identifiedbyoperationid):
  274. ---------------------------------------------------
  275. 1-filter(TO_NUMBER("BUSINESS_DATE")=20090201)-->这里可以看到产生了类型转换
  276. -->高效:
  277. SELECT*
  278. FROMacc_pos_int_tbl
  279. WHEREbusiness_date='20090201'
  280. 6)索引列上使用NULL
  281. ISNULLISNOTNULL会限制索引的使用,因为数据中没有值等于NULL值,即便是NULL值也不等于NULL值.且NULL值不存储在于索引之中
  282. 因此应尽可能避免在索引类上使用NULL
  283. SELECTacc_num
  284. ,pl_cd
  285. ,order_qty
  286. ,trade_date
  287. FROMtrade_client_tbl
  288. WHEREinput_dateISNOTNULL;
  289. ExecutionPlan
  290. ----------------------------------------------------------
  291. Planhashvalue:901462645
  292. --------------------------------------------------------------------------------------
  293. |Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|
  294. --------------------------------------------------------------------------------------
  295. |0|SELECTSTATEMENT||1|44|15(0)|00:00:01|
  296. |*1|TABLEACCESSFULL|TRADE_CLIENT_TBL|1|44|15(0)|00:00:01|
  297. --------------------------------------------------------------------------------------
  298. altertabletrade_client_tblmodify(input_datenotnull);
  299. 不推荐使用的查询方式
  300. SELECT*FROMtable_nameWHEREcolISNOTNULL
  301. SELECT*FROMtable_nameWHEREcolISNULL
  302. 推荐使用的方式
  303. SELECT*FROMtable_nameWHEREcol>=0--尽可能的使用=,>=,<=,like等运算符
  304. -->Author:RobinsonCheng
  305. -->Blog:http://blog.csdn.net/robinson_0612

三、总结
1、尽可能最小化基表数据以及中间结果集(通过过滤条件避免后续产生不必要的计算与聚合)
2、为where子句中的谓词信息提供最佳的访问路径(rowid访问,索引访问)
3、使用合理的SQL写法来避免过多的Oracle内部开销以提高性能
4、合理的使用提示以提高表之间的连接来提高连接效率(如避免迪卡尔集,将不合理的嵌套连接改为hash连接等)

四、更多参考

Oracle SQL tuning 步骤

启用用户进程跟踪

父游标、子游标及共享游标

绑定变量及其优缺点

dbms_xplan之display_cursor函数的使用

dbms_xplan之display函数的使用

执行计划中各字段各模块描述

使用 EXPLAIN PLAN 获取SQL语句执行计划

启用 AUTOTRACE 功能

函数使得索引列失效

Oracle 绑定变量窥探

PL/SQL 联合数组与嵌套表

分享到:
评论

相关推荐

    YOLO算法-数据集数据集-330张图像带标签-椅子-书桌.zip

    YOLO系列算法目标检测数据集,包含标签,可以直接训练模型和验证测试,数据集已经划分好,包含数据集配置文件data.yaml,适用yolov5,yolov8,yolov9,yolov7,yolov10,yolo11算法; 包含两种标签格:yolo格式(txt文件)和voc格式(xml文件),分别保存在两个文件夹中,文件名末尾是部分类别名称; yolo格式:<class> <x_center> <y_center> <width> <height>, 其中: <class> 是目标的类别索引(从0开始)。 <x_center> 和 <y_center> 是目标框中心点的x和y坐标,这些坐标是相对于图像宽度和高度的比例值,范围在0到1之间。 <width> 和 <height> 是目标框的宽度和高度,也是相对于图像宽度和高度的比例值; 【注】可以下拉页面,在资源详情处查看标签具体内容;

    java毕设项目之ssm蜀都天香酒楼的网站设计与实现+jsp(完整前后端+说明文档+mysql+lw).zip

    项目包含完整前后端源码和数据库文件 环境说明: 开发语言:Java 框架:ssm,mybatis JDK版本:JDK1.8 数据库:mysql 5.7 数据库工具:Navicat11 开发软件:eclipse/idea Maven包:Maven3.3 服务器:tomcat7

    weixin138社区互助养老+ssm(论文+源码)-kaic.zip

    weixin138社区互助养老+ssm(论文+源码)_kaic.zip

    光纤到户及通信基础设施报装申请表.docx

    光纤到户及通信基础设施报装申请表.docx

    java毕设项目之ssm基于jsp的精品酒销售管理系统+jsp(完整前后端+说明文档+mysql+lw).zip

    项目包含完整前后端源码和数据库文件 环境说明: 开发语言:Java 框架:ssm,mybatis JDK版本:JDK1.8 数据库:mysql 5.7 数据库工具:Navicat11 开发软件:eclipse/idea Maven包:Maven3.3 服务器:tomcat7

    功能完善的电商数据智能爬虫采集系统项目全套技术资料.zip

    功能完善的电商数据智能爬虫采集系统项目全套技术资料.zip

    YOLO算法-刀数据集-198张图像带标签-刀-枪.zip

    YOLO系列算法目标检测数据集,包含标签,可以直接训练模型和验证测试,数据集已经划分好,包含数据集配置文件data.yaml,适用yolov5,yolov8,yolov9,yolov7,yolov10,yolo11算法; 包含两种标签格:yolo格式(txt文件)和voc格式(xml文件),分别保存在两个文件夹中,文件名末尾是部分类别名称; yolo格式:<class> <x_center> <y_center> <width> <height>, 其中: <class> 是目标的类别索引(从0开始)。 <x_center> 和 <y_center> 是目标框中心点的x和y坐标,这些坐标是相对于图像宽度和高度的比例值,范围在0到1之间。 <width> 和 <height> 是目标框的宽度和高度,也是相对于图像宽度和高度的比例值; 【注】可以下拉页面,在资源详情处查看标签具体内容;

    Android程序开发初级教程WORD文档doc格式最新版本

    ### Android程序开发初级教程(一):初识Android **平台概述** Google推出的Android操作系统平台已经正式亮相,这是一个基于Linux内核的开源操作系统。对于开发者而言,了解其架构和支持的开发语言至关重要。以下是Android平台的架构概览: **平台架构及功能** 1. **应用框架(Application Framework)**:包含可重用和可替换的组件,确保所有软件在该层面上的平等性。 2. **Dalvik虚拟机(Dalvik Virtual Machine)**:一个基于Linux的虚拟机,为Android应用提供运行环境。 3. **集成浏览器(Integrated Browser)**:基于开源WebKit引擎的浏览器,位于应用层。 4. **优化图形(Optimized Graphics)**:包括自定义的2D图形库和遵循OpenGL ES 1.0标准的3D实现。 5. **SQLite数据库**:用于数据存储。 6. **多媒体支持(Media Support)**:支持通用音频、视频以及多种图片格式(如MPEG4, H.264

    【组合数学答案】组合数学-苏大李凡长版-课后习题答案

    内容概要:本文档是《组合数学答案-网络流传版.pdf》的内容,主要包含了排列组合的基础知识以及一些经典的组合数学题目。这些题目涵盖了从排列数计算、二项式定理的应用到容斥原理的实际应用等方面。通过对这些题目的解析,帮助读者加深对组合数学概念和技巧的理解。 适用人群:适合初学者和有一定基础的学习者。 使用场景及目标:可以在学习组合数学课程时作为练习题参考,也可以在复习考试或准备竞赛时使用,目的是提高解决组合数学问题的能力。 其他说明:文档中的题目覆盖了组合数学的基本知识点,适合逐步深入学习。每个题目都有详细的解答步骤,有助于读者掌握解题思路和方法。

    .net core mvc在线考试系统asp.net考试系统源码考试管理系统 主要技术: 基于.net core mvc架构和sql server数据库,数据库访问采用EF core code fir

    .net core mvc在线考试系统asp.net考试系统源码考试管理系统 主要技术: 基于.net core mvc架构和sql server数据库,数据库访问采用EF core code first,前端采用vue.js和bootstrap。 功能模块: 系统包括前台和后台两个部分,分三种角色登录。 管理员登录后台,拥有科目管理,题库管理,考试管理,成绩管理,用户管理等功能。 教师登录后台,可进行题库管理,考试管理和成绩管理。 用户登录前台,可查看考试列表,参加考试,查看已考试的结果,修改密码等。 系统实现了国际化,支持中英两种语言。 源码打包: 包含全套源码,数据库文件,需求分析和代码说明文档。 运行环境: 运行需vs2019或者以上版本,sql server2012或者以上版本。

    YOLO算法-易拉罐识别数据集-512张图像带标签-可口可乐.zip

    YOLO系列算法目标检测数据集,包含标签,可以直接训练模型和验证测试,数据集已经划分好,包含数据集配置文件data.yaml,适用yolov5,yolov8,yolov9,yolov7,yolov10,yolo11算法; 包含两种标签格:yolo格式(txt文件)和voc格式(xml文件),分别保存在两个文件夹中,文件名末尾是部分类别名称; yolo格式:<class> <x_center> <y_center> <width> <height>, 其中: <class> 是目标的类别索引(从0开始)。 <x_center> 和 <y_center> 是目标框中心点的x和y坐标,这些坐标是相对于图像宽度和高度的比例值,范围在0到1之间。 <width> 和 <height> 是目标框的宽度和高度,也是相对于图像宽度和高度的比例值; 【注】可以下拉页面,在资源详情处查看标签具体内容;

    (175415460)基于SpringBoot的通用管理系统源码+数据库+项目文档,前后端分离的通用管理系统模版,可用于开发毕业设计

    包含了登陆注册、用户管理、部门管理、文件管理、权限管理、日志管理、个人中心、数据字典和代码生成这九个功能模块 系统采用了基于角色的访问控制,角色和菜单关联,一个角色可以配置多个菜单权限;然后再将用户和角色关联,一位用户可以赋予多个角色。这样用户就可以根据角色拿到该有的菜单权限,更方便管理者进行权限管控。 本系统还封装了文件管理功能,在其他模块如若要实现图片/文件上传预览时,前端只需导入现成的 Vue 组件即可实现(使用 viewerjs 依赖实现),后端只需定义 String 类型的实体类变量即可,无需再去研究文件上传预览的相关功能,简化了开发者的工作量。内容来源于网络分享,如有侵权请联系我删除。另外如果没有积分的同学需要下载,请私信我。

    三相10Kw光伏并网逆变器 包含全套理图 PCB 源代码

    三相10Kw光伏并网逆变器。包含全套理图 PCB 源代码

    GJB 5236-2004 军用软件质量度量

    GJB 5236-2004 军用软件质量度量文档,本称准规定了车用软件产品的质重模型和基本的度量。本标准为确定车用软件质量需求和衡量军用 软件产品的能力提供了一个框架。

    (179941432)基于MATLAB车牌识别系统【GUI含界面】.zip

    基于MATLAB车牌识别系统【GUI含界面】.zip。内容来源于网络分享,如有侵权请联系我删除。另外如果没有积分的同学需要下载,请私信我。

    (9546452)宿舍管理系统

    【宿舍管理系统】是一种专为高校或住宿机构设计的信息化解决方案,旨在提高宿舍管理的效率和准确性。该系统包含了多项核心功能,如宿舍管理员管理、宿舍信息维护、查询、卫生检查以及电费缴纳等,旨在实现全面的宿舍运营自动化。 **宿舍管理员管理**功能允许指定的管理员进行用户权限分配和角色设定。这包括对管理员账户的创建、修改和删除,以及设置不同的操作权限,例如只读、编辑或管理员权限。通过这样的权限控制,可以确保数据的安全性和管理的规范性。 **宿舍添加与管理**是系统的基础模块。管理员可以录入宿舍的基本信息,如宿舍号、楼栋、楼层、房间类型(单人间、双人间等)、容纳人数、设施配置等。此外,系统还支持批量导入或导出宿舍信息,方便数据的备份和迁移。 **查询功能**是系统的重要组成部分,它允许管理员和学生根据不同的条件(如宿舍号、楼栋、学生姓名等)快速查找宿舍信息。此外,系统还可以生成各种统计报告,如宿舍占用率、空闲宿舍数量等,以便于决策者进行资源优化。 **卫生检查**功能则是对宿舍卫生状况进行定期评估。管理员可设定检查计划,包括检查周期、评分标准等,并记录每次检查的结果。系统能自动生成卫生报表,用于

    YOLO算法-包装好的服装数据集-654张图像带标签-.zip

    YOLO系列算法目标检测数据集,包含标签,可以直接训练模型和验证测试,数据集已经划分好,包含数据集配置文件data.yaml,适用yolov5,yolov8,yolov9,yolov7,yolov10,yolo11算法; 包含两种标签格:yolo格式(txt文件)和voc格式(xml文件),分别保存在两个文件夹中,文件名末尾是部分类别名称; yolo格式:<class> <x_center> <y_center> <width> <height>, 其中: <class> 是目标的类别索引(从0开始)。 <x_center> 和 <y_center> 是目标框中心点的x和y坐标,这些坐标是相对于图像宽度和高度的比例值,范围在0到1之间。 <width> 和 <height> 是目标框的宽度和高度,也是相对于图像宽度和高度的比例值; 【注】可以下拉页面,在资源详情处查看标签具体内容;

    九缸星形发动机点火器3D

    九缸星形发动机点火器3D

    小程序毕业设计项目-音乐播放器

    本项目可以作为小程序毕设项目,主要功能为音乐播放器,主要功能是:可以播放歌曲(采用mp3网络连接实现)、专辑封面播放时可以旋转,能够实现开始和暂停播放,可以点击下一首歌曲,主页面实现动态轮播图

    出差审批单(表格模板).docx

    出差审批单(表格模板).docx

Global site tag (gtag.js) - Google Analytics