ITPub博客

首页 > Linux操作系统 > Linux操作系统 > ORACLE SQL 优化简述

ORACLE SQL 优化简述

原创 Linux操作系统 作者:flyingzs 时间:2013-03-08 17:42:47 0 删除 编辑

一、编写高效SQL语句

  1. 1) 选择最有效的表名顺序(仅适用于RBO模式)                                                                              
  2.     ORACLE的解析器总是按照从右到左的顺序处理FROM子句中的表名,因此FROM子句中最后的一个表将作为驱动表被优先处理。当FROM子句       
  3. 存在多个表的时候,应当考虑将表上记录最少的那个表置于FROM的最右端作为基表。Oracle会首先扫描基表(FROM子句中最后的那个表)并对       
  4. 记录进行排序,然后扫描第二个表(FROM子句中最后第二个表),最后将所有从第二个表中检索出的记录与第一个表中合适记录进行合并。如       
  5. 果有3个以上的表连接查询, 那就需要选择交叉表(intersection table)作为基础表,交叉表是指那个被其他表所引用的表。                 
  6.                                                                                                                                  
  7. 下面的例子使用最常见的scott或hr模式下的表进行演示                                
  8.                                                                                                
  9. 表 EMP 有14条记录                                                                
  10. 表 DEPT 有4条记录                                                   
  11. SELECT  /*+ rule */ COUNT( * )  FROM   emp, dept;          --高效的写法    
  12.                                                                                                                                  
  13. scott@CNMMBO> set autotrace traceonly stat;                                                     
  14. scott@CNMMBO> SELECT  /*+ rule */ COUNT( * )  FROM   emp, dept;                        
  15.                                                                              
  16. Elapsed: 00:00:00.14                                                         
  17.                                                                                      
  18. Statistics                                                                         
  19. ----------------------------------------------------------                                
  20.           1  recursive calls                                                            
  21.           0  db block gets                                                             
  22.          35  consistent gets                                                        
  23.           0  physical reads                                                 
  24.           0  redo size                                                               
  25.         515  bytes sent via SQL*Net to client                                    
  26.         492  bytes received via SQL*Net from client                             
  27.           2  SQL*Net roundtrips to/from client                            
  28.           0  sorts (memory)                                                       
  29.           0  sorts (disk)                                                             
  30.           1  rows processed                                                                   
  31.                                                                                              
  32. SELECT  /*+ rule */ COUNT( * )  FROM   dept, emp;         --低效的写法            
  33. scott@CNMMBO> SELECT  /*+ rule */ COUNT( * )  FROM   dept, emp;                             
  34.                                                                                           
  35. Elapsed: 00:00:00.02                                                                           
  36.                                           
  37. Statistics                                                                                   
  38. ----------------------------------------------------------                                
  39.           1  recursive calls                                                         
  40.           0  db block gets                                                                
  41.         105  consistent gets                                                              
  42.           0  physical reads                                                              
  43.           0  redo size                                                                 
  44.         515  bytes sent via SQL*Net to client                                    
  45.         492  bytes received via SQL*Net from client                                    
  46.           2  SQL*Net roundtrips to/from client                                           
  47.           0  sorts (memory)                                                           
  48.           0  sorts (disk)                                                           
  49.           1  rows processed                                                                 
  50.                                                                                               
  51. 2) select 查询中避免使用'*'                                                               
  52.    当你想在SELECT子句中列出所有的COLUMN时,使用动态SQL列引用 '*' 是一个方便的方法.不幸的是,这是一个非常低效的方法.实际       
  53. 上,ORACLE在解析的过程中, 会将 '*' 依次转换成所有的列名, 这个工作是通过查询数据字典完成的, 这意味着将耗费更多的时间。         
  54. 注:本文中的例子出于简化演示而使用了select * ,生产环境应避免使用.                                
  55.                                                                                   
  56. 3) 减少访问数据库的次数                                                   
  57.     每当执行一条SQL语句,Oracle 需要完成大量的内部操作,象解析SQL语句,估算索引的利用率,绑定变量, 读数据块等等.由此可       
  58. 见,减少访问数据库的次数,实际上是降低了数据库系统开销                              
  59. -->下面通过3种方式来获得雇员编号为7788与7902的相关信息                           
  60.                                                                              
  61. -->方式 1 (最低效):                                                       
  62. select ename,job,sal from emp where empno=7788;                            
  63.                                                                              
  64. select ename,job,sal from emp where empno=7902;                                    
  65.                                                                        
  66. -->方式 2 (次低效):                                                              
  67. -->下面使用了参数游标来完成,每传递一次参数则需要对表emp访问一次,增加了I/O                           
  68.   DECLARE                                                                              
  69.     CURSOR C1(E_NO NUMBER)  IS                                                     
  70.     SELECT ename, job, sal                                                          
  71.     FROM emp                                                                    
  72.     WHERE empno = E_NO;                                                      
  73.   BEGIN                                                                        
  74.     OPEN C1 (7788);                                                           
  75.     FETCH C1 INTO …, …, …;                                                      
  76.     ..                                                                     
  77.     OPEN C1 (7902);                                                     
  78.     FETCH C1 INTO …, …, …;                                                   
  79.     CLOSE C1;                                                                
  80.   END;                                                                           
  81.                                                                                   
  82. -->方式 3 (最高效)                                             
  83. SELECT a.ename                                                              
  84.      , a.job                                                     
  85.      , a.sal                                                        
  86.      , b.ename                                                        
  87.      , b.job                                                           
  88.      , b.sal                                                        
  89. FROM   emp a, emp b                                                      
  90. WHERE  a.empno = 7788 OR b.empno = 7902;                                      
  91.                                                         
  92. 注意:在SQL*Plus,SQL*Forms和Pro*C中重新设置ARRAYSIZE参数,可以增加每次数据库访问的检索数据量,建议值为200.       
  93.                                                                  
  94. 4) 使用DECODE函数来减少处理时间                                
  95. -->使用decode函数可以避免重复扫描相同的行或重复连接相同的表                   
  96. select count(*),sum(sal) from emp where deptno=20 and ename like 'SMITH%';              
  97.                                                                                           
  98. select count(*),sum(sal) from emp where deptno=30 and ename like 'SMITH%';                
  99.                                                                                               
  100. -->通过使用decode函数一次扫描即可完成所有满足条件记录的处理                                        
  101. SELECT COUNT( DECODE( deptno, 20, 'x', NULL ) ) d20_count                                         
  102.      , COUNT( DECODE( deptno, 30, 'x', NULL ) ) d30_count                                      
  103.      , SUM( DECODE( deptno, 20, sal, NULL ) ) d20_sal                                          
  104.      , SUM( DECODE( deptno, 30, sal, NULL ) ) d30_sal                                            
  105. FROM   emp                                                                  
  106. WHERE  ename LIKE 'SMITH%';                                                
  107.                                                                           
  108. 类似的,DECODE函数也可以运用于GROUP BY 和ORDER BY子句中。                           
  109.                                                                                  
  110. 5) 整合简单,无关联的数据库访问                                        
  111. -->如果你有几个简单的数据库查询语句,你可以把它们整合到一个查询中以提高性能(即使它们之间没有关系)          
  112. -->整合前                                                                           
  113. SELECT name                                                                            
  114. FROM   emp                                                                          
  115. WHERE  empno = 1234;                                                              
  116.                                                                                
  117. SELECT name                                                                     
  118. FROM   dept                                                          
  119. WHERE  deptno = 10;                                                        
  120.                                                                                             
  121. SELECT name                                                                 
  122. FROM   cat                                                               
  123. WHERE  cat_type = 'RD';                                                                   
  124.                                                                                           
  125. -->整合后                                                                       
  126. SELECT e.name, d.name, c.name                                                                     
  127. FROM   cat c                                                                                         
  128.      , dpt d                                                                                        
  129.      , emp e                                                                                        
  130.      , dual x                                                                                     
  131. WHERE      NVL( 'X', x.dummy ) = NVL( 'X', e.ROWID(+) )                          
  132.        AND NVL( 'X', x.dummy ) = NVL( 'X', d.ROWID(+) )           
  133.        AND NVL( 'X', x.dummy ) = NVL( 'X', c.ROWID(+) )            
  134.        AND e.emp_no(+) = 1234                                                                     
  135.        AND d.dept_no(+) = 10                                                                     
  136.        AND c.cat_type(+) = 'RD';                                                                   
  137.                                                                                          
  138. -->从上面的SQL语句可以看出,尽管三条语句被整合为一条,性能得以提高,然可读性差,此时应权衡性能与代价              
  139.                                                                                                                                  
  140. 6) 删除重复记录                                                                                   
  141. -->通过使用rowid来作为过滤条件,性能高效                                               
  142. DELETE FROM emp e                                                                     
  143. WHERE  e.ROWID > (SELECT MIN( x.ROWID )     
  144.                   FROM   emp x                                                       
  145.                   WHERE  x.empno = e.empno);                                                   
  146.                                                                                     
  147. 7) 使用truncate 代替 delete                                                               
  148. -->通常情况下,任意记录的删除需要在回滚段构造删除前镜像以实现回滚(rollback).对于未提交的数据在执行rollback之后,Oracle会生成     
  149. -->等价SQL语句去恢复记录(如delete,则生成对应的insert语句;如insert则生成对应的delete;如update,则是同时生成delete和insert  
  150. -->使用truncate命令则是执行DDL命令,不产生任何回滚信息,直接格式化并释放高水位线.故该语句性能高效.由于不能rollback,因此慎用.   
  151.                                                                                                  
  152. 8) 尽量多使用COMMIT(COMMIT应确保事务的完整性)             
  153. -->只要有可能,在程序中尽量多使用COMMIT,这样程序的性能得到提高,需求也会因为COMMIT所释放的资源而减少              
  154. -->COMMIT所释放的资源:                                                                                                          
  155. -->1.回滚段上用于恢复数据的信息                                                                                                  
  156. -->2.释放语句处理期间所持有的锁                                                                                                  
  157. -->3.释放redo log buffer占用的空间(commit将redo log buffer中的entries 写入到联机重做日志文件)                          
  158. -->4.ORACLE为管理上述3种资源中的内部开销                                                                                         
  159.                                                                                                                                  
  160. 9) 计算记录条数                                                                                                                  
  161. -->一般的情况下,count(*)比count(1)稍快.如果可以通过索引检索,对索引列的计数是最快的,因为直接扫描索引即可,例如COUNT(EMPNO)   
  162. -->实际情况是经测试上述三种情况并无明显差异.                                                   
  163.                                                                                                 
  164. 10) 用Where子句替换HAVING子句                                                                                                    
  165. -->尽可能的避免having子句,因为HAVING 子句是对检索出所有记录之后再对结果集进行过滤。这个处理需要排序,总计等操作                 
  166. -->通过WHERE子句则在分组之前即可过滤不必要的记录数目,从而减少聚合的开销                                                         
  167.                                                                                                                                  
  168. -->低效:                                                                                
  169. SELECT deptno, AVG( sal )                                                                 
  170. FROM   emp                                                                                              
  171. GROUP BY deptno                                                                                          
  172. HAVING deptno = 20;                                                                                          
  173.                                                                                             
  174. scott@CNMMBO> SELECT deptno, AVG( sal )                                                                      
  175.   2  FROM   emp                                                                                       
  176.   3  GROUP BY deptno                                                                                  
  177.   4  HAVING deptno= 20;                                                                                   
  178.                                                                                               
  179. Statistics                      
  180. ----------------------------------------------------------     
  181.           0  recursive calls                                                                           
  182.           0  db block gets                                                                 
  183.           7  consistent gets                                                               
  184.           0  physical reads                                                                
  185.           0  redo size                                                                       
  186.         583  bytes sent via SQL*Net to client                                                  
  187.         492  bytes received via SQL*Net from client                                                 
  188.           2  SQL*Net roundtrips to/from client                                                       
  189.           0  sorts (memory)                                                                      
  190.           0  sorts (disk)                                                                     
  191.           1  rows processed                                                                
  192. -->高效:                                                                
  193. SELECT deptno, AVG( sal )          
  194. FROM   emp                                                                                      
  195. WHERE  deptno = 20                                                                                  
  196. GROUP BY deptno;                                                                   
  197.                                                                                       
  198. scott@CNMMBO> SELECT deptno, AVG( sal )                  
  199.   2  FROM   emp                                                                             
  200.   3  WHERE  deptno = 20                                                                   
  201.   4  GROUP BY deptno;                                                                   
  202.                                                                                          
  203. Statistics                                          
  204. ----------------------------------------------------------        
  205.           0  recursive calls                                                              
  206.           0  db block gets                                                               
  207.           2  consistent gets                                                               
  208.           0  physical reads                                                            
  209.           0  redo size                                                                
  210.         583  bytes sent via SQL*Net to client                                         
  211.         492  bytes received via SQL*Net from client                                      
  212.           2  SQL*Net roundtrips to/from client                                               
  213.           0  sorts (memory)                                                                      
  214.           0  sorts (disk)                                                                          
  215.           1  rows processed                                                                     
  216.                                                                                         
  217. 11) 最小化表查询次数                                                                                                             
  218. -->在含有子查询的SQL语句中,要特别注意减少对表的查询                                                                             
  219. -->低效:                                                           
  220. SELECT *                                                                                      
  221. FROM   employees                                                                                   
  222. WHERE  department_id = (SELECT department_id                                                       
  223.                         FROM   departments                                                     
  224.                         WHERE  department_name = 'Marketing')                                   
  225.        AND manager_id = (SELECT manager_id                                                       
  226.                          FROM   departments                                                     
  227.                          WHERE  department_name = 'Marketing');                                  
  228. -->高效:                                                                
  229. SELECT *                                                                                    
  230. FROM   employees                                                                             
  231. WHERE  ( department_id, manager_id ) = (SELECT department_id, manager_id                                  
  232.                                         FROM   departments                                                   
  233.                                         WHERE  department_name = 'Marketing')                   
  234.                                                                                     
  235. -->类似更新多列的情形                
  236. -->低效:                     
  237. UPDATE employees                                                                                  
  238. SET    job_id = ( SELECT MAX( job_id ) FROM jobs ), salary = ( SELECT AVG( min_salary ) FROM jobs )           
  239. WHERE  department_id = 10;                                                                    
  240.                                                                                         
  241. -->高效:                  
  242. UPDATE employees           
  243. SET    ( job_id, salary ) = ( SELECT MAX( job_id ), AVG( min_salary ) FROM jobs )       
  244. WHERE  department_id = 10;                                                              
  245.                                                                           
  246. 12) 使用表别名                                                                        
  247. -->在多表查询时,为所返回列使用表别名作为前缀以减少解析时间以及那些相同列歧义引起的语法错误                                      
  248.                                                                                  
  249. 13) 用EXISTS替代IN                                                                                   
  250.     在一些基于基础表的查询中,为了满足一个条件,往往需要对另一个表进行联接.在这种情况下,使用EXISTS(或NOT EXISTS)通常        
  251. 将提高查询的效率.                                                                                                               
  252. -->低效:                                 
  253. SELECT *                                                              
  254. FROM   emp                                                       
  255. WHERE  sal > 1000                                                      
  256.        AND deptno IN (SELECT deptno                                         
  257.                       FROM   dept                                          
  258.                       WHERE  loc = 'DALLAS')                                     
  259.                                                                    
  260. -->高效:                                                                               
  261. SELECT *                                                                 
  262. FROM   emp                                                             
  263. WHERE  empno > 1000                                          
  264.        AND EXISTS                                                       
  265.               (SELECT 1                                       
  266.                FROM   dept                                     
  267.                WHERE  deptno = emp.deptno AND loc = 'DALLAS')                           
  268.                      
  269. 14) 用NOT EXISTS替代NOT IN     
  270.     在子查询中,NOT IN子句引起一个内部的排序与合并.因此,无论何时NOT IN子句都是最低效的,因为它对子查询中的表执行了一个全表        
  271. 遍历.为避免该情形,应当将其改写成外部连接(OUTTER JOIN)或适用NOT EXISTS                                    
  272. -->低效:                                                                      
  273. SELECT *                                                                                        
  274. FROM   emp                                                                        
  275. WHERE  deptno NOT IN (SELECT deptno                                          
  276.                        FROM   dept                                            
  277.                        WHERE  loc = 'DALLAS');                                             
  278.                                     
  279. -->高效:                                             
  280. SELECT e.*                                                                                      
  281. FROM   emp e                                                                                      
  282. WHERE  NOT EXISTS                                                                                     
  283.           (SELECT 1                                                                                    
  284.            FROM   dept                                                                                
  285.            WHERE  deptno = e.deptno AND loc = 'DALLAS');                                       
  286.                                                                       
  287. -->最高效(尽管下面的查询最高效,并不推荐使用,因为列loc使用了不等运算,当表dept数据量较大,且loc列存在索引的话,则此时索引失效)   
  288. SELECT e.*                                                                                     
  289. FROM   emp e LEFT JOIN dept d ON e.deptno = d.deptno                                                 
  290. WHERE  d.loc <> 'DALLAS'                                                                       
  291.                                                             
  292. 15) 使用表连接替换EXISTS                                                
  293. 一般情况下,使用表连接比EXISTS更高效                                                             
  294. -->低效:                                                    
  295. SELECT *                                                                                                       
  296. FROM   employees e                                                                                         
  297. WHERE  EXISTS                                                                                                      
  298.           (SELECT 1                                                                       
  299.            FROM   departments                                                              
  300.            WHERE  department_id = e.department_id AND department_name = 'IT');                                 
  301.                                                                                     
  302. -->高效:                  
  303. SELECT *              -->经测试此写法SQLplus下比上面的写法多一次逻辑读,而在Toad下两者结果一致               
  304. FROM   employees e INNER JOIN departments d ON d.department_id = e.department_id              
  305. WHERE  d.department_name = 'IT';                                                        
  306.                                                               
  307. 16) 用EXISTS替换DISTINCT          
  308. 对于一对多关系表信息查询时(如部门表和雇员表),应避免在select 子句中使用distinct,而使用exists来替换           
  309.                                                      
  310. -->低效:                                                                
  311. SELECT DISTINCT e.department_id, d.department_name                                              
  312. FROM   departments d INNER JOIN employees e ON d.department_id = e.department_id;                   
  313.                                   
  314. -->高效:                                                            
  315. SELECT d.department_id,department_name                                                         
  316. from departments d                                                                       
  317. WHERE  EXISTS                                                                              
  318.           (SELECT 1                                                                      
  319.            FROM   employees e                                                       
  320.            WHERE  d.department_id=e.department_id);                                              
  321.                                                                       
  322. EXISTS 使查询更为迅速,因为RDBMS核心模块将在子查询的条件一旦满足后,立刻返回结果                                
  323. -->经测试此写法SQLplus下比上面的写法多一次逻辑读,而在Toad下两者结果一致                                  
  324.                                                               
  325. 17) 使用 UNION ALL 替换 UNION(如果有可能的话)                                                           
  326. 当SQL语句需要UNION两个查询结果集时,这两个结果集合会以UNION-ALL的方式被合并, 然后在输出最终结果前进行排序。       
  327. 如果用UNION ALL替代UNION, 这样排序就不是必要了。 效率就会因此得到提高。                                                         
  328.                                                                 
  329. 注意:                       
  330. UNION ALL会输出所有的结果集,而UNION则过滤掉重复记录并对其进行排序.因此在使用时应考虑业务逻辑是否允许当前的结果集存在重复现象   
  331.                                                                          
  332. 寻找低效的SQL语句                                                            
  333. -->下面的语句主要适用于从视图v$sqlarea中获得当前运行下且耗用buffer_gets较多的SQL语句                   
  334. SELECT executions                                                                     
  335.      , disk_reads                                                                    
  336.      , buffer_gets                                                                  
  337.      , ROUND( ( buffer_gets         
  338.                - disk_reads )       
  339.              / buffer_gets, 2 )      
  340.           hit_ratio                                      
  341.      , ROUND( disk_reads / executions, 2 ) reads_per_run                   
  342.      , sql_text                                       
  343. FROM   v$sqlarea                                                               
  344. WHERE      executions > 0                                                   
  345.        AND buffer_gets > 0                                               
  346.        AND ( buffer_gets                                                    
  347.             - disk_reads )                                                  
  348.            / buffer_gets < 0.80                                                        
  349. ORDER BY 4 DESC;                                                   
  350.                               
  351. 18) 尽可能避免使用函数,函数会导致更多的 recursive calls   

  352. 1) 避免基于索引列的计算                                                                                                           
  353. where 子句中的谓词上存在索引,而此时基于该列的计算将使得索引失效                                                                  
  354.                                                                                                                                   
  355. -->低效:                      
  356. SELECT employee_id, first_name                                                                                                    
  357. FROM   employees                                                                                                                  
  358. WHERE  employee_id + 10 > 150;        -->索引列上使用了计算,因此索引失效,走全表扫描方式                                         
  359.                                                                                                                                   
  360. -->高效:                              
  361. SELECT employee_id, first_name                                                                                                    
  362. FROM   employees                                                                                                                  
  363. WHERE  employee_id > 160;    -->走索引范围扫描方式                                                                                
  364.                          
  365. 例外情形      
  366. 上述规则不适用于SQL中的MIN和MAX函数                                                                                               
  367. hr@CNMMBO> SELECT MAX( employee_id ) max_id                                                                                       
  368.   2  FROM   employees                                                                                                             
  369.   3  WHERE  employee_id                                                                                                           
  370.   4         + 10 > 150;                                                                                                           
  371.                                                                                                                                   
  372. 1 row selected.                                                                                                                   
  373.                                                                                                                                   
  374. Execution Plan                                                                                                                    
  375. ----------------------------------------------------------         
  376. Plan hash value: 1481384439                                 
  377. ---------------------------------------------------------------------------------------------                
  378. | Id  | Operation                   | Name          | Rows  | Bytes | Cost (%CPU)| Time     |                  
  379. ---------------------------------------------------------------------------------------------                 
  380. |   0 | SELECT STATEMENT            |               |     1 |     4 |     1   (0)| 00:00:01 |                  
  381. |   1 |  SORT AGGREGATE             |               |     1 |     4 |            |          |                   
  382. |   2 |   FIRST ROW                 |               |     5 |    20 |     1   (0)| 00:00:01 |           
  383. |*  3 |    INDEX FULL SCAN (MIN/MAX)| EMP_EMP_ID_PK |     5 |    20 |     1   (0)| 00:00:01 |          
  384. ---------------------------------------------------------------------------------------------              
  385.                                                                                                                                   
  386. 2) 避免在索引列上使用NOT运算或不等于运算(<>,!=)                                                                                   
  387. 通常,我们要避免在索引列上使用NOT或<>,两者会产生在和在索引列上使用函数相同的影响。 当ORACLE遇到NOT或不等运算时,他就会停止        
  388. 使用索引转而执行全表扫描。                                                                                                        
  389.                                                                                                                                   
  390. -->低效:                                                                                 
  391. SELECT *                                                                                                                          
  392. FROM   emp                                                                                                                        
  393. WHERE  NOT ( deptno = 20 );   -->实际上NOT ( deptno = 20 )等同于deptno <> 20,即deptno <>同样会限制索引                            
  394.                                                                                                                                   
  395. -->高效:                                                                           
  396. SELECT *                                                                                                                          
  397. FROM   emp                                                                                                                        
  398. WHERE  deptno > 20 OR deptno < 20;                                                                                                
  399. -->尽管此方式可以替换且实现上述结果,但依然走全表扫描,如果是单纯的 > 或 < 运算,则此时为索引范围扫描                             
  400.                                                                                                                                   
  401. 需要注意的是,在某些时候, ORACLE优化器会自动将NOT转化成相对应的关系操作符                                                        
  402. 其次如果是下列运算符进行NOT运算,依然有可能选择走索引, 仅仅除了NOT = 之外,因为 NOT = 等价于 <>                                     
  403.                                                                                                                                   
  404. “NOT >”   to <=                                                                                                                 
  405. “NOT >=”  to <                                                                                                                  
  406. “NOT <”   to >=                                                                                                                 
  407. “NOT <=”  to >                                                                                                                  
  408.                                                                                                                                   
  409. 来看一个实际的例子                                                                                                                
  410. hr@CNMMBO> SELECT *                                                                                                               
  411.   2  FROM   employees                                                                                                             
  412.   3  where not employee_id<100; -->索引列上使用了not,但是该查询返回了所有的记录,即107条,因此此时选择走全表扫描                  
  413.                                                                                                                                   
  414. 107 rows selected.                                                                                                                
  415.                                                                                                                                   
  416. Execution Plan                                                                                                                    
  417. ----------------------------------------------------------    
  418. Plan hash value: 1445457117                                                                                                       
  419. -------------------------------------------------------------------------------             
  420. | Id  | Operation         | Name      | Rows  | Bytes | Cost (%CPU)| Time     |          
  421. -------------------------------------------------------------------------------       
  422. |   0 | SELECT STATEMENT  |           |   107 |  7276 |     3   (0)| 00:00:01 |    
  423. |*  1 |  TABLE ACCESS FULL| EMPLOYEES |   107 |  7276 |     3   (0)| 00:00:01 | -->执行计划中使用了走全表扫描方式        
  424. -------------------------------------------------------------------------------                                            
  425. Predicate Information (identified by operation id):                                                
  426. ---------------------------------------------------       
  427.        
  428.    1 - filter("EMPLOYEE_ID">=100)           -->查看这里的谓词信息被自动转换为 >= 运算符                    
  429.                                                                                                                                   
  430. hr@CNMMBO> SELECT *                                                                                                               
  431.   2  FROM   employees                                                                                                             
  432.   3  where not employee_id<140; -->此例与上面的语句相同,仅仅是查询范围不同返回67条记录,而此时选择了索引范围扫描                   
  433.                                                                                                                                   
  434. 67 rows selected.                                                                                                                 
  435.                                                                                                                                   
  436. Execution Plan                                                                                                                    
  437. ----------------------------------------------------------          
  438. Plan hash value: 603312277                                                                                                        
  439.                                                                                                                                   
  440. ---------------------------------------------------------------------------------------------               
  441. | Id  | Operation                   | Name          | Rows  | Bytes | Cost (%CPU)| Time     |             
  442. ---------------------------------------------------------------------------------------------            
  443. |   0 | SELECT STATEMENT            |               |    68 |  4624 |     3   (0)| 00:00:01 |           
  444. |   1 |  TABLE ACCESS BY INDEX ROWID| EMPLOYEES     |    68 |  4624 |     3   (0)| 00:00:01 |               
  445. |*  2 |   INDEX RANGE SCAN          | EMP_EMP_ID_PK |    68 |       |     1   (0)| 00:00:01 | -->索引范围扫描方式    
  446. ---------------------------------------------------------------------------------------------                           
  447. Predicate Information (identified by operation id):                                                
  448. ---------------------------------------------------                                
  449.     2 - access("EMPLOYEE_ID">=140)                                        
  450.                                                                                                                                   
  451. 3) 用UNION 替换OR(适用于索引列)                                                                                                   
  452.     通常情况下,使用UNION 替换WHERE子句中的OR将会起到较好的效果.基于索引列使用OR使得优化器倾向于使用全表扫描,而不是扫描索引.      
  453.     注意,以上规则仅适用于多个索引列有效。 如果有column没有被索引, 查询效率可能会因为你没有选择OR而降低。                         
  454. -->低效:                             
  455. SELECT deptno, dname                                                                                                              
  456. FROM   dept                                                                                                                       
  457. WHERE  loc = 'DALLAS' OR deptno = 20;                                                                                             
  458.                                                                                                                                   
  459. -->高效:                                       
  460. SELECT deptno, dname                                                                                                              
  461. FROM   dept                                                                                                                       
  462. WHERE  loc = 'DALLAS'                                                                                                             
  463. UNION                                                                                                                             
  464. SELECT deptno, dname                                                                                                              
  465. FROM   dept                                                                                                                       
  466. WHERE  deptno = 30                                                                                                                
  467.                                                                                                                                   
  468. -->经测试,由于数据量较少,此时where子句中的谓词上都存在索引列时,两者性能相当.                                                    
  469. -->假定where子句中存在两列      
  470. scott@CNMMBO> create table t6 as select object_id,owner,object_name from dba_objects where owner='SYS' and rownum<1001;           
  471.                                                                                                                                   
  472. scott@CNMMBO> insert into t6 select object_id,owner,object_name from dba_objects where owner='SCOTT' and rownum<6;                
  473.                                                                                                                                   
  474. scott@CNMMBO> create index i_t6_object_id on t6(object_id);                                                  
  475.                                                                                                                                   
  476. scott@CNMMBO> create index i_t6_owner on t6(owner);                                                
  477.                                                                                                                                   
  478. scott@CNMMBO> insert into t6 select object_id,owner,object_name from dba_objects where owner='SYSTEM' and rownum<=300;            
  479.                                                                                                                                   
  480. scott@CNMMBO> commit;                                                                                                             
  481.                                                                                                                                   
  482. scott@CNMMBO> exec dbms_stats.gather_table_stats('SCOTT','T6',cascade=>true);    
  483.                                                                                                                                   
  484. scott@CNMMBO> select owner,count(*) from t6 group by owner;        
  485.                                                               
  486. OWNER                  COUNT(*)                                                  
  487. -------------------- ----------                                                    
  488. SCOTT                         5                                                         
  489. SYSTEM                      300                                                           
  490. SYS                        1000                                                          
  491.                                                                                                                                   
  492. scott@CNMMBO> select * from t6 where owner='SCOTT' and rownum<2;                                                                  
  493.                                                                             
  494.  OBJECT_ID OWNER                OBJECT_NAME                                                 
  495. ---------- -------------------- --------------------                                            
  496.      69450 SCOTT                T_TEST                                                         
  497.                                                                                 
  498. scott@CNMMBO> select * from t6 where object_id=69450 or owner='SYSTEM';                                                           
  499.                                                                                                                                   
  500. 301 rows selected.                                                                                                                
  501.                                                                                                                                   
  502. Execution Plan                                                                                                                    
  503. ----------------------------------------------------------       
  504. Plan hash value: 238853296                                                                                                       
  505. -----------------------------------------------------------------------------------------------            
  506. | Id  | Operation                    | Name           | Rows  | Bytes | Cost (%CPU)| Time     |           
  507. -----------------------------------------------------------------------------------------------        
  508. |   0 | SELECT STATEMENT             |                |   300 |  7200 |     5   (0)| 00:00:01 |         
  509. |   1 |  CONCATENATION               |                |       |       |            |          |      
  510. |   2 |   TABLE ACCESS BY INDEX ROWID| T6             |     1 |    24 |     2   (0)| 00:00:01 |              
  511. |*  3 |    INDEX RANGE SCAN          | I_T6_OBJECT_ID |     1 |       |     1   (0)| 00:00:01 |             
  512. |*  4 |   TABLE ACCESS BY INDEX ROWID| T6             |   299 |  7176 |     3   (0)| 00:00:01 |             
  513. |*  5 |    INDEX RANGE SCAN          | I_T6_OWNER     |   300 |       |     1   (0)| 00:00:01 |                
  514. -----------------------------------------------------------------------------------------------                
  515.                                                                                                                                   
  516. Predicate Information (identified by operation id):                                                                               
  517. ---------------------------------------------------                                 
  518.    3 - access("OBJECT_ID"=69450)                    
  519.    4 - filter(LNNVL("OBJECT_ID"=69450))             
  520.    5 - access("OWNER"='SYSTEM')                  
  521.                                                                                                                                   
  522. Statistics                                                                                                                        
  523. ----------------------------------------------------------    
  524.           0  recursive calls                                                                                                      
  525.           0  db block gets                                                                                                        
  526.          46  consistent gets                                                                                                      
  527.           0  physical reads                                                                                                       
  528.           0  redo size                                                                                                            
  529.       11383  bytes sent via SQL*Net to client                                                                                     
  530.         712  bytes received via SQL*Net from client                                                                               
  531.          22  SQL*Net roundtrips to/from client                                                                                    
  532.           0  sorts (memory)                                                                                                       
  533.           0  sorts (disk)                                                                                                         
  534.         301  rows processed                                                                                                       
  535.                                                                                                                                   
  536. scott@CNMMBO> select * from t6 where owner='SYSTEM' or object_id=69450;                                                           
  537.                                                                                                                                   
  538. 301 rows selected.                                                                                                                
  539.                                                                                                                                   
  540. Execution Plan                                                                                                                    
  541. ----------------------------------------------------------    
  542. Plan hash value: 238853296                                                                                                        
  543. -----------------------------------------------------------------------------------------------              
  544. | Id  | Operation                    | Name           | Rows  | Bytes | Cost (%CPU)| Time     |               
  545. -----------------------------------------------------------------------------------------------            
  546. |   0 | SELECT STATEMENT             |                |   300 |  7200 |     5   (0)| 00:00:01 |              
  547. |   1 |  CONCATENATION               |                |       |       |            |          |                
  548. |   2 |   TABLE ACCESS BY INDEX ROWID| T6             |     1 |    24 |     2   (0)| 00:00:01 |                
  549. |*  3 |    INDEX RANGE SCAN          | I_T6_OBJECT_ID |     1 |       |     1   (0)| 00:00:01 |                  
  550. |*  4 |   TABLE ACCESS BY INDEX ROWID| T6             |   299 |  7176 |     3   (0)| 00:00:01 |          
  551. |*  5 |    INDEX RANGE SCAN          | I_T6_OWNER     |   300 |       |     1   (0)| 00:00:01 |          
  552. -----------------------------------------------------------------------------------------------              
  553.                                                                                                                                   
  554. Predicate Information (identified by operation id):                                                                               
  555. ---------------------------------------------------      
  556.    3 - access("OBJECT_ID"=69450)                                       
  557.    4 - filter(LNNVL("OBJECT_ID"=69450))                                   
  558.    5 - access("OWNER"='SYSTEM')                                     
  559.                                                                                                                                   
  560. Statistics                                                              
  561. ----------------------------------------------------------                   
  562.           1  recursive calls                                                                                                      
  563.           0  db block gets                                                                                                        
  564.          46  consistent gets                                                                                                      
  565.           0  physical reads                                                                                                       
  566.           0  redo size                                                                                                            
  567.       11383  bytes sent via SQL*Net to client                                                                                     
  568.         712  bytes received via SQL*Net from client                                                                               
  569.          22  SQL*Net roundtrips to/from client                                                                                    
  570.           0  sorts (memory)                                                                                                       
  571.           0  sorts (disk)                                                                                                         
  572.         301  rows processed                                                                                                       
  573.                                                                                                                                   
  574. scott@CNMMBO> select * from t6                                                                                                    
  575.   2  where object_id=69450                                                                                                        
  576.   3  union                                                                                                                        
  577.   4  select * from t6                                                                                                             
  578.   5  where owner='SYSTEM';                                                                                                        
  579.                                                                                                                                   
  580. 301 rows selected.                                                                                                                
  581.                                                                                                                                   
  582. Execution Plan                                                                                                                    
  583. ----------------------------------------------------------    
  584. Plan hash value: 370530636                                                                                                        
  585. ------------------------------------------------------------------------------------------------           
  586. | Id  | Operation                     | Name           | Rows  | Bytes | Cost (%CPU)| Time     |           
  587. ------------------------------------------------------------------------------------------------          
  588. |   0 | SELECT STATEMENT              |                |   301 |  7224 |     7  (72)| 00:00:01 |           
  589. |   1 |  SORT UNIQUE                  |                |   301 |  7224 |     7  (72)| 00:00:01 |         
  590. |   2 |   UNION-ALL                   |                |       |       |            |          |         
  591. |   3 |    TABLE ACCESS BY INDEX ROWID| T6             |     1 |    24 |     2   (0)| 00:00:01 |          
  592. |*  4 |     INDEX RANGE SCAN          | I_T6_OBJECT_ID |     1 |       |     1   (0)| 00:00:01 |      
  593. |   5 |    TABLE ACCESS BY INDEX ROWID| T6             |   300 |  7200 |     3   (0)| 00:00:01 |         
  594. |*  6 |     INDEX RANGE SCAN          | I_T6_OWNER     |   300 |       |     1   (0)| 00:00:01 |      
  595. ------------------------------------------------------------------------------------------------                
  596.                                                                                                                                   
  597. Predicate Information (identified by operation id):                                                                               
  598. ---------------------------------------------------                                                                               
  599.    4 - access("OBJECT_ID"=69450)                         
  600.    6 - access("OWNER"='SYSTEM')                                      
  601.                                                                                                                                   
  602. Statistics                                                                                                                        
  603. ----------------------------------------------------------                                                                        
  604.           1  recursive calls                                                                                                      
  605.           0  db block gets                                                                                                        
  606.           7  consistent gets                                                                                                      
  607.           0  physical reads                                                                                                       
  608.           0  redo size                                                                                                            
  609.       11383  bytes sent via SQL*Net to client                                                                                     
  610.         712  bytes received via SQL*Net from client                                                                               
  611.          22  SQL*Net roundtrips to/from client                                                                                    
  612.           1  sorts (memory)                                                                                                       
  613.           0  sorts (disk)                                                                                                         
  614.         301  rows processed                                                                                                       
  615.                                                                                                                                   
  616. -->从上面的统计信息可知,consistent gets由46下降为7,故当where子句中谓词上存在索引时,使用union替换or更高效                       
  617. -->即使当列object_id与owner上不存在索引时,使用union仍然比or更高效(在Oracle 10g R2与Oracle 11g R2测试)                            
  618.                                                                                                                                   
  619. 4) 避免索引列上使用函数                                                                                                           
  620. -->下面是一个来自实际生产环境的例子                                                                                               
  621. -->表acc_pos_int_tbl上business_date列存在索引,由于使用了SUBSTR函数,此时索引失效,使用全表扫描                                    
  622. SELECT acc_num                                                                                                                    
  623.      , curr_cd                                                                                                                    
  624.      , DECODE( '20110728'                                                
  625.              , ( SELECT TO_CHAR( LAST_DAY( TO_DATE( '20110728', 'YYYYMMDD' ) ), 'YYYYMMDD' ) FROM dual ), 0       
  626.              ,   adj_credit_int_lv1_amt                      
  627.                + adj_credit_int_lv2_amt                            
  628.                - adj_debit_int_lv1_amt                               
  629.                - adj_debit_int_lv2_amt )                                  
  630.           AS interest                                               
  631. FROM   acc_pos_int_tbl                                          
  632. WHERE  SUBSTR( business_date, 1, 6 ) = SUBSTR( '20110728', 1, 6 ) AND business_date <= '20110728';       
  633.                                                                                                                                   
  634. -->改进的办法             
  635. SELECT acc_num                                             
  636.      , curr_cd                                           
  637.      , DECODE( '20110728'                                  
  638.              , ( SELECT TO_CHAR( LAST_DAY( TO_DATE( '20110728', 'YYYYMMDD' ) ), 'YYYYMMDD' ) FROM dual ), 0    
  639.              ,   adj_credit_int_lv1_amt                      
  640.                + adj_credit_int_lv2_amt                         
  641.                - adj_debit_int_lv1_amt                            
  642.                - adj_debit_int_lv2_amt )                             
  643.           AS interest                                       
  644. FROM   acc_pos_int_tbl acc_pos_int_tbl                                               
  645. WHERE  business_date >= TO_CHAR( LAST_DAY( ADD_MONTHS( TO_DATE( '20110728', 'yyyymmdd' ), -1 ) )    
  646.                                 + 1, 'yyyymmdd' )                        
  647.        AND business_date <= '20110728';                   
  648.                                                                                                                                   
  649. -->下面的例子虽然没有使用函数,但字符串连接同样导致索引失效                                                                       
  650. -->低效:                      
  651. SELECT account_name, amount                                                                                                       
  652. FROM   transaction                                                                                                                
  653. WHERE  account_name                                                                                                               
  654.        || account_type = 'AMEXA';                                                                                                 
  655.                                                                                                                                   
  656. -->高效:                          
  657. SELECT account_name, amount                                                                                                       
  658. FROM   transaction                                                                                                                
  659. WHERE  account_name = 'AMEX' AND account_type = 'A';                                                                              
  660.                                                                                                                                   
  661. 5) 比较不匹配的数据类型                                                                                                           
  662. -->下面的查询中business_date列上存在索引,且为字符型,这种                                                                          
  663. -->低效:                                    
  664. SELECT *                                                                                                                          
  665. FROM   acc_pos_int_tbl                                                                                                            
  666. WHERE  business_date = 20090201;                                                                                                 
  667.                                                                                                                                   
  668. Execution Plan                                                                                                                    
  669. ----------------------------------------------------------        
  670. Plan hash value: 2335235465                      
  671.                                                  
  672. -------------------------------------------------------------------------------------                 
  673. | Id  | Operation         | Name            | Rows  | Bytes | Cost (%CPU)| Time     |                         
  674. -------------------------------------------------------------------------------------                        
  675. |   0 | SELECT STATEMENT  |                 | 37516 |  2857K|   106K  (1)| 00:21:17 |                      
  676. |*  1 |  TABLE ACCESS FULL| ACC_POS_INT_TBL | 37516 |  2857K|   106K  (1)| 00:21:17 |                 
  677. -------------------------------------------------------------------------------------                    
  678.                                                                                                                                   
  679. Predicate Information (identified by operation id):                          
  680. ---------------------------------------------------         
  681.      1 - filter(TO_NUMBER("BUSINESS_DATE")=20090201)    -->这里可以看到产生了类型转换             
  682.                                                                                                                                   
  683. -->高效:                                        
  684. SELECT *                                                                                                                          
  685. FROM   acc_pos_int_tbl                                                                                                            
  686. WHERE  business_date = '20090201'                                                                                                 
  687.                                                                                                                                   
  688. 6) 索引列上使用 NULL 值             
  689.     IS NULL和IS NOT NULL会限制索引的使用,因为数据中没有值等于NULL值,即便是NULL值也不等于NULL值.且NULL值不存储在于索引之中    
  690. 因此应尽可能避免在索引类上使用NULL值                                                                                              
  691.                                                                                                                                   
  692. SELECT acc_num                                                                                                                    
  693.      , pl_cd                                                                                                                      
  694.      , order_qty                                                                                                                  
  695.      , trade_date                                                                                                                 
  696. FROM   trade_client_tbl                                                                                                           
  697. WHERE  input_date IS NOT NULL;                                                                                                    
  698.                                                                                                                                   
  699. Execution Plan                                              
  700. ----------------------------------------------------------                          
  701. Plan hash value: 901462645                                        
  702. --------------------------------------------------------------------------------------                   
  703. | Id  | Operation         | Name             | Rows  | Bytes | Cost (%CPU)| Time     |                     
  704. --------------------------------------------------------------------------------------                   
  705. |   0 | SELECT STATEMENT  |                  |     1 |    44 |    15   (0)| 00:00:01 |                
  706. |*  1 |  TABLE ACCESS FULL| TRADE_CLIENT_TBL |     1 |    44 |    15   (0)| 00:00:01 |                     
  707. --------------------------------------------------------------------------------------                   
  708.                                             
  709. alter table trade_client_tbl modify (input_date not null);            
  710.                                                                      
  711. 不推荐使用的查询方式                                        
  712. SELECT * FROM table_name WHERE col IS NOT NULL                        
  713.                                                                   
  714. SELECT * FROM table_name WHERE col IS NULL                                 
  715.                                                                                                                                   
  716. 推荐使用的方式                        
  717. SELECT * FROM table_name WHERE col >= 0 --尽可能的使用 =, >=, <=, like 等运算符
  718.      

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/26243632/viewspace-755620/ ,如需转载,请注明出处,否则将追究法律责任。

上一篇: Oracle RBO、CBO简介
请登录后发表评论 登录
全部评论

注册时间:2013-03-06

  • 博文量
    10
  • 访问量
    10824