在写这篇文章之前,xxx已经写过了几篇关于改查询语句主题的文章,想要了解的朋友可以去翻一下之前的文章

    No SQL,No cost. SQL语句是成造数据库销开最大的部份。而不良SQL写法直接致导数据库系统性能下降的况情是皆比比。那么如何才能称得

    上高效的SQL语句呢?一是查询优化器为当前的SQL语句成生最好的执行计划,证保数据读写应用最好路径;二是置设理合的物理存储构结,如表

    的类型,字段的序顺,字段的数据类型等。本文重要描述如何编写高效的SQL语句并给出示例。面下的描述重要分为三个部份,一是编写高效SQL

    语句,二是应用索引高提查询性能的部份,三是结总部份。

 

    一、编写高效SQL语句

    




[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        
1) 择选最有效的表名序顺(仅适于用RBO模式)                                                                            
ORACLE的剖析器是总按照从右到左的序顺处置FROM句子中的表名,因此FROM句子中最后的一个表将作为驱动表被优先处置。当FROM句子
存在多个表的时候,应该虑考将表上录记起码的那个表置于FROM的最右端作为基表。Oracle会首先描扫基表(FROM句子中最后的那个表)并对
录记停止排序,然后描扫第二个表(FROM句子中最后第二个表),最后将有所从第二个表中检索出的录记与第一个表中适合录记停止合并。如
果有3个以上的表连接查询, 那就要需择选叉交表(intersection table)作为基本表,叉交表是指那个被其他表所引用的表。

面下的例子应用最常见的scott或hr模式下的表停止演示

表 EMP 有14条录记
表 DEPT 有4条录记
SELECT /*+ rule */ COUNT( * ) FROM emp, dept; --高效的写法

scott@CNMMBO> set autotrace traceonly stat;
scott@CNMMBO> SELECT /*+ rule */ COUNT( * ) FROM emp, dept;

Elapsed: 00:00:00.14

Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
35 consistent gets
0 physical reads
0 redo size
515 bytes sent via SQL*Net to client
492 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed

SELECT /*+ rule */ COUNT( * ) FROM dept, emp; --低效的写法
scott@CNMMBO> SELECT /*+ rule */ COUNT( * ) FROM dept, emp;

Elapsed: 00:00:00.02

Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
105 consistent gets
0 physical reads
0 redo size
515 bytes sent via SQL*Net to client
492 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed

2) select 查询中免避应用'*'
当你想在SELECT句子中列出有所的COLUMN时,应用动态SQL列引用 '*' 是一个便利的方法.幸不的是,这是一个非常低效的方法.现实
上,ORACLE在剖析的过程当中, 会将 '*' 次依转换成有所的列名, 这个任务是通过查询数据字典成完的, 这意味着将消耗更多的间时。
注:本文中的例子出于简化演示而应用了select * ,生产境环应免避应用.

3) 加增拜访数据库的数次
每当执行一条SQL语句,Oracle 要需成完大批的外部作操,象剖析SQL语句,预算索引的利用率,绑定变量, 读数据块等等.由此可
见,加增拜访数据库的数次,现实上是降低了数据库系统销开
-->面下通过3种方法来得获员雇编号为7788与7902的关相息信

-->方法 1 (最低效):
select ename,job,sal from emp where empno=7788;

select ename,job,sal from emp where empno=7902;

-->方法 2 (次低效):
-->面下应用了参数游标来成完,每递传一次参数则要需对表emp拜访一次,加增了I/O
DECLARE
CURSOR C1(E_NO NUMBER) IS
SELECT ename, job, sal
FROM emp
WHERE empno = E_NO;
BEGIN
OPEN C1 (7788);
FETCH C1 INTO …, …, …;
..
OPEN C1 (7902);
FETCH C1 INTO …, …, …;
CLOSE C1;
END;

-->方法 3 (最高效)
SELECT a.ename
, a.job
, a.sal
, b.ename
, b.job
, b.sal
FROM emp a, emp b
WHERE a.empno = 7788 OR b.empno = 7902;

注意:在SQL*Plus,SQL*Forms和Pro*C中新重置设ARRAYSIZE参数,可以加增每数次据库拜访的检索数据量,提议值为200.

4) 应用DECODE函数来加增处置间时
-->应用decode函数可以免避重复描扫雷同的行或重复连接雷同的表
select count(*),sum(sal) from emp where deptno=20 and ename like 'SMITH%';

select count(*),sum(sal) from emp where deptno=30 and ename like 'SMITH%';

-->通过应用decode函数一次描扫可即成完有所足满件条录记的处置
SELECT COUNT( DECODE( deptno, 20, 'x', NULL ) ) d20_count
, COUNT( DECODE( deptno, 30, 'x', NULL ) ) d30_count
, SUM( DECODE( deptno, 20, sal, NULL ) ) d20_sal
, SUM( DECODE( deptno, 30, sal, NULL ) ) d30_sal
FROM emp
WHERE ename LIKE 'SMITH%';

似类的,DECODE函数也可以运于用GROUP BY 和ORDER BY句子中。

5) 整合单简,无关联的数据库拜访
-->如果你有几个单简的数据库查询语句,你可以把它们整合到一个查询中以高提性能(即使它们之间没有关系)
-->整合前
SELECT name
FROM emp
WHERE empno = 1234;

SELECT name
FROM dept
WHERE deptno = 10;

SELECT name
FROM cat
WHERE cat_type = 'RD';

-->整合后
SELECT e.name, d.name, c.name
FROM cat c
, dpt d
, emp e
, dual x
WHERE NVL( 'X', x.dummy ) = NVL( 'X', e.ROWID(+) )
AND NVL( 'X', x.dummy ) = NVL( 'X', d.ROWID(+) )
AND NVL( 'X', x.dummy ) = NVL( 'X', c.ROWID(+) )
AND e.emp_no(+) = 1234
AND d.dept_no(+) = 10
AND c.cat_type(+) = 'RD';

-->从上面的SQL语句可以看出,尽管三条语句被整合为一条,性能得以高提,然可读性差,此时应权衡性能与价值

6) 删除重复录记
-->通过应用rowid来作为过滤件条,性能高效
DELETE FROM emp e
WHERE e.ROWID > (SELECT MIN( x.ROWID )
FROM emp x
WHERE x.empno = e.empno);

7) 应用truncate 取代 delete
-->常通况情下,意任录记的删除要需在回滚段结构删除前像镜以实现回滚(rollback).对于未交提的数据在执行rollback以后,Oracle会成生
-->等价SQL语句去复恢录记(如delete,则成生对应的insert语句;如insert则成生对应的delete;如update,则是同时成生delete和insert
-->应用truncate命令则是执行DDL命令,不生产任何回滚息信,直接格式化并释放高水线位.故该语句性能高效.由于不能rollback,因此慎用.

8) 尽量多应用COMMIT(COMMIT应确保事务的完整性)
-->只要有可能,在序程中尽量多应用COMMIT,这样序程的性能到得高提,需求也会因为COMMIT所释放的资源而加增
-->COMMIT所释放的资源:
-->1.回滚段上于用复恢数据的息信
-->2.释放语句处置期间所持有的锁
-->3.释放redo log buffer占用的间空(commit将redo log buffer中的entries 写入到联机重做日志文件)
-->4.ORACLE为理管上述3种资源中的外部销开

9) 盘算录记条数
-->一般的况情下,count(*)比count(1)稍快.如果可以通过索引检索,对索引列的计数是最快的,因为直接描扫索引可即,例如COUNT(EMPNO)
-->现实况情是经试测上述三种况情并无显著异差.

10) 用Where句子替换HAVING句子
-->尽可能的免避having句子,因为HAVING 句子是对检索出有所录记以后再对结果集停止过滤。这个处置要需排序,总计等作操
-->通过WHERE句子则在组分之前可即过滤不必要的录记目数,从而加增聚合的销开

-->低效:
SELECT deptno, AVG( sal )
FROM emp
GROUP BY deptno
HAVING deptno = 20;

scott@CNMMBO> SELECT deptno, AVG( sal )
2 FROM emp
3 GROUP BY deptno
4 HAVING deptno= 20;

Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
7 consistent gets
0 physical reads
0 redo size
583 bytes sent via SQL*Net to client
492 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
-->高效:
SELECT deptno, AVG( sal )
FROM emp
WHERE deptno = 20
GROUP BY deptno;

scott@CNMMBO> SELECT deptno, AVG( sal )
2 FROM emp
3 WHERE deptno = 20
4 GROUP BY deptno;

Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
2 consistent gets
0 physical reads
0 redo size
583 bytes sent via SQL*Net to client
492 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed

11) 最小化表查询数次
-->在含有子查询的SQL语句中,要特别注意加增对表的查询
-->低效:
SELECT *
FROM employees
WHERE department_id = (SELECT department_id
FROM departments
WHERE department_name = 'Marketing')
AND manager_id = (SELECT manager_id
FROM departments
WHERE department_name = 'Marketing');
-->高效:
SELECT *
FROM employees
WHERE ( department_id, manager_id ) = (SELECT department_id, manager_id
FROM departments
WHERE department_name = 'Marketing')

-->似类更新多列的况情
-->低效:
UPDATE employees
SET job_id = ( SELECT MAX( job_id ) FROM jobs ), salary = ( SELECT AVG( min_salary ) FROM jobs )
WHERE department_id = 10;

-->高效:
UPDATE employees
SET ( job_id, salary ) = ( SELECT MAX( job_id ), AVG( min_salary ) FROM jobs )
WHERE department_id = 10;

12) 应用表别名
-->在多表查询时,为所返回列应用表别名作为前缀以加增剖析间时以及那些雷同列歧义起引的语法错误

13) 用EXISTS取代IN
在一些基于基本表的查询中,为了足满一个件条,常常要需对另一个表停止联接.在这类况情下,应用EXISTS(或NOT EXISTS)常通
将高提查询的效率.
-->低效:
SELECT *
FROM emp
WHERE sal > 1000
AND deptno IN (SELECT deptno
FROM dept
WHERE loc = 'DALLAS')

-->高效:
SELECT *
FROM emp
WHERE empno > 1000
AND EXISTS
(SELECT 1
FROM dept
WHERE deptno = emp.deptno AND loc = 'DALLAS')

14) 用NOT EXISTS取代NOT IN
在子查询中,NOT IN句子起引一个外部的排序与合并.因此,无论何时NOT IN句子都是最低效的,因为它对子查询中的表执行了一个全表
遍历.为免避该况情,应该将其改写成外部连接(OUTTER JOIN)或实用NOT EXISTS
-->低效:
SELECT *
FROM emp
WHERE deptno NOT IN (SELECT deptno
FROM dept
WHERE loc = 'DALLAS');

-->高效:
SELECT e.*
FROM emp e
WHERE NOT EXISTS
(SELECT 1
FROM dept
WHERE deptno = e.deptno AND loc = 'DALLAS');

-->最高效(尽管面下的查询最高效,不并推荐应用,因为列loc应用了不等算运,当表dept数据量较大,且loc列存在索引的话,则此时索引效失)
SELECT e.*
FROM emp e LEFT JOIN dept d ON e.deptno = d.deptno
WHERE d.loc <> 'DALLAS'

15) 应用表连接替换EXISTS
一般况情下,应用表连接比EXISTS更高效
-->低效:
SELECT *
FROM employees e
WHERE EXISTS
(SELECT 1
FROM departments
WHERE department_id = e.department_id AND department_name = 'IT');

-->高效:
SELECT * -->经试测此写法SQLplus下比上面的写法多一次逻辑读,而在Toad下两者结果分歧
FROM employees e INNER JOIN departments d ON d.department_id = e.department_id
WHERE d.department_name = 'IT';

16) 用EXISTS替换DISTINCT
对于一对多关系表息信查询时(如部门表和员雇表),应免避在select 句子中应用distinct,而应用exists来替换

-->低效:
SELECT DISTINCT e.department_id, d.department_name
FROM departments d INNER JOIN employees e ON d.department_id = e.department_id;

-->高效:
SELECT d.department_id,department_name
from departments d
WHERE EXISTS
(SELECT 1
FROM employees e
WHERE d.department_id=e.department_id);

EXISTS 使查询更为速迅,因为RDBMS核心块模将在子查询的件条一旦足满后,立刻返回结果
-->经试测此写法SQLplus下比上面的写法多一次逻辑读,而在Toad下两者结果分歧

17) 应用 UNION ALL 替换 UNION(如果有可能的话)
当SQL语句要需UNION两个查询结果集时,这两个结果集合会以UNION-ALL的方法被合并, 然后在输出最终结果前停止排序。
如果用UNION ALL取代UNION, 这样排序就不是必要了。 效率就会因此到得高提。

注意:
UNION ALL会输出有所的结果集,而UNION则过滤掉重复录记并对其停止排序.因此在应用时应虑考业务逻辑是否允许当前的结果集存在重复现象

寻找低效的SQL语句
-->面下的语句重要适于用从视图v$sqlarea中得获当前运行下且耗用buffer_gets较多的SQL语句
SELECT executions
, disk_reads
, buffer_gets
, ROUND( ( buffer_gets
- disk_reads )
/ buffer_gets, 2 )
hit_ratio
, ROUND( disk_reads / executions, 2 ) reads_per_run
, sql_text
FROM v$sqlarea
WHERE executions > 0
AND buffer_gets > 0
AND ( buffer_gets
- disk_reads )
/ buffer_gets < 0.80
ORDER BY 4 DESC;

18) 尽可能免避应用函数,函数会致导更多的 recursive calls


     

    二、理合应用索引以高提性能

       索引依赖于表而存在,是真实表的一个缩影,似类于一本书的目录,通过目录以更快得获所需的结果。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作操应用索引上存在碎片而失去高度均衡,因此定期的重构索引是有必要的.



    




[sql] ​ ​​ ​

  1. 1) 免避基于索引列的盘算                                                                                                           
  2. where 句子中的谓词上存在索引,而此时基于该列的盘算将使得索引效失                                                                  
  3.                                                                                                                                   
  4. -->低效:                       
  5. SELECT employee_id, first_name                                                                                                    
  6. FROM   employees                                                                                                                  
  7. WHERE  employee_id + 10 > 150;        -->索引列上应用了盘算,因此索引效失,走全表描扫方法                                         
  8.                                                                                                                                   
  9. -->高效:                              
  10. SELECT employee_id, first_name                                                                                                    
  11. FROM   employees                                                                                                                  
  12. WHERE  employee_id > 160;    -->走索引范围描扫方法                                                                                
  13.                          
  14. 例外况情      
  15. 上述规则不适于用SQL中的MIN和MAX函数                                                                                               
  16. hr@CNMMBO> SELECT MAX( employee_id ) max_id                                                                                       
  17.   2  FROM   employees                                                                                                             
  18.   3  WHERE  employee_id                                                                                                           
  19.   4         + 10 > 150;                                                                                                           
  20.                                                                                                                                   
  21. 1 row selected.                                                                                                                   
  22.                                                                                                                                   
  23. Execution Plan                                                                                                                    
  24. ----------------------------------------------------------         
  25. Plan hash value: 1481384439                                 
  26. ---------------------------------------------------------------------------------------------                
  27. | Id  | Operation                   | Name          | Rows  | Bytes | Cost (%CPU)| Time     |                  
  28. ---------------------------------------------------------------------------------------------                 
  29. |   0 | SELECT STATEMENT            |               |     1 |     4 |     1   (0)| 00:00:01 |                  
  30. |   1 |  SORT AGGREGATE             |               |     1 |     4 |            |          |                   
  31. |   2 |   FIRST ROW                 |               |     5 |    20 |     1   (0)| 00:00:01 |           
  32. |*  3 |    INDEX FULL SCAN (MIN/MAX)| EMP_EMP_ID_PK |     5 |    20 |     1   (0)| 00:00:01 |          
  33. ---------------------------------------------------------------------------------------------              
  34.                                                                                                                                   
  35. 2) 免避在索引列上应用NOT算运或不等于算运(<>,!=)                                                                                   
  36. 常通,我们要免避在索引列上应用NOT或<>,两者会生产在和在索引列上应用函数雷同的影响。 当ORACLE遇到NOT或不等算运时,他就会停止        
  37. 应用索引转而执行全表描扫。                                                                                                        
  38.                                                                                                                                   
  39. -->低效:                                                                                 
  40. SELECT *                                                                                                                          
  41. FROM   emp                                                                                                                        
  42. WHERE  NOT ( deptno = 20 );   -->现实上NOT ( deptno = 20 )等同于deptno <> 20,即deptno <>同样会限制索引                            
  43.                                                                                                                                   
  44. -->高效:                                                                           
  45. SELECT *                                                                                                                          
  46. FROM   emp                                                                                                                        
  47. WHERE  deptno > 20 OR deptno < 20;                                                                                                
  48. -->尽管此方法可以替换且实现上述结果,但依然走全表描扫,如果是单纯的 > 或 < 算运,则此时为索引范围描扫                             
  49.                                                                                                                                   
  50. 要需注意的是,在某些时候, ORACLE优化器会自动将NOT转化成相对应的关系作操符                                                        
  51. 其次如果是下列算运符停止NOT算运,依然有可能择选走索引, 仅仅除了NOT = 之外,因为 NOT = 等价于 <>                                     
  52.                                                                                                                                   
  53. “NOT >”   to <=                                                                                                                 
  54. “NOT >=”  to <                                                                                                                  
  55. “NOT <”   to >=                                                                                                                 
  56. “NOT <=”  to >                                                                                                                  
  57.                                                                                                                                   
  58. 来看一个现实的例子                                                                                                                
  59. hr@CNMMBO> SELECT *                                                                                                               
  60.   2  FROM   employees                                                                                                             
  61.   3  where not employee_id<100; -->索引列上应用了not,但是该查询返回了有所的录记,即107条,因此此时择选走全表描扫                  
  62.                                                                                                                                   
  63. 107 rows selected.                                                                                                                
  64.                                                                                                                                   
  65. Execution Plan                                                                                                                    
  66. ----------------------------------------------------------    
  67. Plan hash value: 1445457117                                                                                                       
  68. -------------------------------------------------------------------------------             
  69. | Id  | Operation         | Name      | Rows  | Bytes | Cost (%CPU)| Time     |          
  70. -------------------------------------------------------------------------------       
  71. |   0 | SELECT STATEMENT  |           |   107 |  7276 |     3   (0)| 00:00:01 |    
  72. |*  1 |  TABLE ACCESS FULL| EMPLOYEES |   107 |  7276 |     3   (0)| 00:00:01 | -->执行计划中应用了走全表描扫方法        
  73. -------------------------------------------------------------------------------                                            
  74. Predicate Information (identified by operation id):                                                
  75. ---------------------------------------------------       
  76.        
  77.    1 - filter("EMPLOYEE_ID">=100)           -->查看这里的谓词息信被自动转换为 >= 算运符                    
  78.                                                                                                                                   
  79. hr@CNMMBO> SELECT *                                                                                                               
  80.   2  FROM   employees                                                                                                             
  81.   3  where not employee_id<140; -->此例与上面的语句雷同,仅仅是查询范围不同返回67条录记,而此时择选了索引范围描扫                   
  82.                                                                                                                                   
  83. 67 rows selected.                                                                                                                 
  84.                                                                                                                                   
  85. Execution Plan                                                                                                                    
  86. ----------------------------------------------------------          
  87. Plan hash value: 603312277                                                                                                        
  88.                                                                                                                                   
  89. ---------------------------------------------------------------------------------------------               
  90. | Id  | Operation                   | Name          | Rows  | Bytes | Cost (%CPU)| Time     |             
  91. ---------------------------------------------------------------------------------------------            
  92. |   0 | SELECT STATEMENT            |               |    68 |  4624 |     3   (0)| 00:00:01 |           
  93. |   1 |  TABLE ACCESS BY INDEX ROWID| EMPLOYEES     |    68 |  4624 |     3   (0)| 00:00:01 |               
  94. |*  2 |   INDEX RANGE SCAN          | EMP_EMP_ID_PK |    68 |       |     1   (0)| 00:00:01 | -->索引范围描扫方法    
  95. ---------------------------------------------------------------------------------------------                           
  96. Predicate Information (identified by operation id):                                                
  97. ---------------------------------------------------                                
  98.     2 - access("EMPLOYEE_ID">=140)                                        
  99.                                                                                                                                   
  100. 3) 用UNION 替换OR(适于用索引列)                                                                                                   
  101.     常通况情下,应用UNION 替换WHERE句子中的OR将会起到较好的效果.基于索引列应用OR使得优化器倾向于应用全表描扫,而不是描扫索引.      
  102.     注意,以上规则仅适于用多个索引列有效。 如果有column没有被索引, 查询效率可能会因为你没有择选OR而降低。                         
  103. -->低效:                             
  104. SELECT deptno, dname                                                                                                              
  105. FROM   dept                                                                                                                       
  106. WHERE  loc = 'DALLAS' OR deptno = 20;                                                                                             
  107.                                                                                                                                   
  108. -->高效:                                       
  109. SELECT deptno, dname                                                                                                              
  110. FROM   dept                                                                                                                       
  111. WHERE  loc = 'DALLAS'                                                                                                             
  112. UNION                                                                                                                             
  113. SELECT deptno, dname                                                                                                              
  114. FROM   dept                                                                                                                       
  115. WHERE  deptno = 30                                                                                                                
  116.                                                                                                                                   
  117. -->经试测,由于数据量较少,此时where句子中的谓词上都存在索引列时,两者性能相当.                                                    
  118. -->假定where句子中存在两列       
  119. scott@CNMMBO> create table t6 as select object_id,owner,object_name from dba_objects where owner='SYS' and rownum<1001;           
  120.                                                                                                                                   
  121. scott@CNMMBO> insert into t6 select object_id,owner,object_name from dba_objects where owner='SCOTT' and rownum<6;                
  122.                                                                                                                                   
  123. scott@CNMMBO> create index i_t6_object_id on t6(object_id);                                                  
  124.                                                                                                                                   
  125. scott@CNMMBO> create index i_t6_owner on t6(owner);                                                
  126.                                                                                                                                   
  127. scott@CNMMBO> insert into t6 select object_id,owner,object_name from dba_objects where owner='SYSTEM' and rownum<=300;            
  128.                                                                                                                                   
  129. scott@CNMMBO> commit;                                                                                                             
  130.                                                                                                                                   
  131. scott@CNMMBO> exec dbms_stats.gather_table_stats('SCOTT','T6',cascade=>true);    
  132.                                                                                                                                   
  133. scott@CNMMBO> select owner,count(*) from t6 group by owner;        
  134.                                                               
  135. OWNER                  COUNT(*)                                                  
  136. -------------------- ----------                                                    
  137. SCOTT                         5                                                         
  138. SYSTEM                      300                                                           
  139. SYS                        1000                                                          
  140.                                                                                                                                   
  141. scott@CNMMBO> select * from t6 where owner='SCOTT' and rownum<2;                                                                  
  142.                                                                             
  143.  OBJECT_ID OWNER                OBJECT_NAME                                                 
  144. ---------- -------------------- --------------------                                            
  145.      69450 SCOTT                T_TEST                                                         
  146.                                                                                 
  147. scott@CNMMBO> select * from t6 where object_id=69450 or owner='SYSTEM';                                                           
  148.                                                                                                                                   
  149. 301 rows selected.                                                                                                                
  150.                                                                                                                                   
  151. Execution Plan                                                                                                                    
  152. ----------------------------------------------------------       
  153. Plan hash value: 238853296                                                                                                       
  154. -----------------------------------------------------------------------------------------------            
  155. | Id  | Operation                    | Name           | Rows  | Bytes | Cost (%CPU)| Time     |           
  156. -----------------------------------------------------------------------------------------------        
  157. |   0 | SELECT STATEMENT             |                |   300 |  7200 |     5   (0)| 00:00:01 |         
  158. |   1 |  CONCATENATION               |                |       |       |            |          |      
  159. |   2 |   TABLE ACCESS BY INDEX ROWID| T6             |     1 |    24 |     2   (0)| 00:00:01 |              
  160. |*  3 |    INDEX RANGE SCAN          | I_T6_OBJECT_ID |     1 |       |     1   (0)| 00:00:01 |             
  161. |*  4 |   TABLE ACCESS BY INDEX ROWID| T6             |   299 |  7176 |     3   (0)| 00:00:01 |             
  162. |*  5 |    INDEX RANGE SCAN          | I_T6_OWNER     |   300 |       |     1   (0)| 00:00:01 |                
  163. -----------------------------------------------------------------------------------------------                
  164.                                                                                                                                   
  165. Predicate Information (identified by operation id):                                                                               
  166. ---------------------------------------------------                                 
  167.    3 - access("OBJECT_ID"=69450)                    
  168.    4 - filter(LNNVL("OBJECT_ID"=69450))             
  169.    5 - access("OWNER"='SYSTEM')                  
  170.                                                                                                                                   
  171. Statistics                                                                                                                        
  172. ----------------------------------------------------------    
  173.           0  recursive calls                                                                                                      
  174.           0  db block gets                                                                                                        
  175.          46  consistent gets                                                                                                      
  176.           0  physical reads                                                                                                       
  177.           0  redo size                                                                                                            
  178.       11383  bytes sent via SQL*Net to client                                                                                     
  179.         712  bytes received via SQL*Net from client                                                                               
  180.          22  SQL*Net roundtrips to/from client                                                                                    
  181.           0  sorts (memory)                                                                                                       
  182.           0  sorts (disk)                                                                                                         
  183.         301  rows processed                                                                                                       
  184.                                                                                                                                   
  185. scott@CNMMBO> select * from t6 where owner='SYSTEM' or object_id=69450;                                                           
  186.                                                                                                                                   
  187. 301 rows selected.                                                                                                                
  188.                                                                                                                                   
  189. Execution Plan                                                                                                                    
  190. ----------------------------------------------------------    
  191. Plan hash value: 238853296                                                                                                        
  192. -----------------------------------------------------------------------------------------------              
  193. | Id  | Operation                    | Name           | Rows  | Bytes | Cost (%CPU)| Time     |               
  194. -----------------------------------------------------------------------------------------------            
  195. |   0 | SELECT STATEMENT             |                |   300 |  7200 |     5   (0)| 00:00:01 |              
  196. |   1 |  CONCATENATION               |                |       |       |            |          |                
  197. |   2 |   TABLE ACCESS BY INDEX ROWID| T6             |     1 |    24 |     2   (0)| 00:00:01 |                
  198. |*  3 |    INDEX RANGE SCAN          | I_T6_OBJECT_ID |     1 |       |     1   (0)| 00:00:01 |                  
  199. |*  4 |   TABLE ACCESS BY INDEX ROWID| T6             |   299 |  7176 |     3   (0)| 00:00:01 |          
  200. |*  5 |    INDEX RANGE SCAN          | I_T6_OWNER     |   300 |       |     1   (0)| 00:00:01 |          
  201. -----------------------------------------------------------------------------------------------              
  202.                                                                                                                                   
  203. Predicate Information (identified by operation id):                                                                               
  204. ---------------------------------------------------      
  205.    3 - access("OBJECT_ID"=69450)                                       
  206.    4 - filter(LNNVL("OBJECT_ID"=69450))                                   
  207.    5 - access("OWNER"='SYSTEM')                                     
  208.                                                                                                                                   
  209. Statistics                                                              
  210. ----------------------------------------------------------                   
  211.           1  recursive calls                                                                                                      
  212.           0  db block gets                                                                                                        
  213.          46  consistent gets                                                                                                      
  214.           0  physical reads                                                                                                       
  215.           0  redo size                                                                                                            
  216.       11383  bytes sent via SQL*Net to client                                                                                     
  217.         712  bytes received via SQL*Net from client                                                                               
  218.          22  SQL*Net roundtrips to/from client                                                                                    
  219.           0  sorts (memory)                                                                                                       
  220.           0  sorts (disk)                                                                                                         
  221.         301  rows processed                                                                                                       
  222.                                                                                                                                   
  223. scott@CNMMBO> select * from t6                                                                                                    
  224.   2  where object_id=69450                                                                                                        
  225.   3  union                                                                                                                        
  226.   4  select * from t6                                                                                                             
  227.   5  where owner='SYSTEM';                                                                                                        
  228.                                                                                                                                   
  229. 301 rows selected.                                                                                                                
  230.                                                                                                                                   
  231. Execution Plan                                                                                                                    
  232. ----------------------------------------------------------    
  233. Plan hash value: 370530636                                                                                                        
  234. ------------------------------------------------------------------------------------------------           
  235. | Id  | Operation                     | Name           | Rows  | Bytes | Cost (%CPU)| Time     |           
  236. ------------------------------------------------------------------------------------------------          
  237. |   0 | SELECT STATEMENT              |                |   301 |  7224 |     7  (72)| 00:00:01 |           
  238. |   1 |  SORT UNIQUE                  |                |   301 |  7224 |     7  (72)| 00:00:01 |         
  239. |   2 |   UNION-ALL                   |                |       |       |            |          |         
  240. |   3 |    TABLE ACCESS BY INDEX ROWID| T6             |     1 |    24 |     2   (0)| 00:00:01 |          
  241. |*  4 |     INDEX RANGE SCAN          | I_T6_OBJECT_ID |     1 |       |     1   (0)| 00:00:01 |      
  242. |   5 |    TABLE ACCESS BY INDEX ROWID| T6             |   300 |  7200 |     3   (0)| 00:00:01 |         
  243. |*  6 |     INDEX RANGE SCAN          | I_T6_OWNER     |   300 |       |     1   (0)| 00:00:01 |      
  244. ------------------------------------------------------------------------------------------------                
  245.                                                                                                                                   
  246. Predicate Information (identified by operation id):                                                                               
  247. ---------------------------------------------------                                                                               
  248.    4 - access("OBJECT_ID"=69450)                         
  249.    6 - access("OWNER"='SYSTEM')                                      
  250.                                                                                                                                   
  251. Statistics                                                                                                                        
  252. ----------------------------------------------------------                                                                        
  253.           1  recursive calls                                                                                                      
  254.           0  db block gets                                                                                                        
  255.           7  consistent gets                                                                                                      
  256.           0  physical reads                                                                                                       
  257.           0  redo size                                                                                                            
  258.       11383  bytes sent via SQL*Net to client                                                                                     
  259.         712  bytes received via SQL*Net from client                                                                               
  260.          22  SQL*Net roundtrips to/from client                                                                                    
  261.           1  sorts (memory)                                                                                                       
  262.           0  sorts (disk)                                                                                                         
  263.         301  rows processed                                                                                                       
  264.                                                                                                                                   
  265. -->从上面的统计息信可知,consistent gets由46下降为7,故当where句子中谓词上存在索引时,应用union替换or更高效                       
  266. -->即使当列object_id与owner上不存在索引时,应用union仍然比or更高效(在Oracle 10g R2与Oracle 11g R2试测)                            
  267.                                                                                                                                   
  268. 4) 免避索引列上应用函数                                                                                                           
  269. -->面下是一个来自现实生产境环的例子                                                                                               
  270. -->表acc_pos_int_tbl上business_date列存在索引,由于应用了SUBSTR函数,此时索引效失,应用全表描扫                                    
  271. SELECT acc_num                                                                                                                    
  272.      , curr_cd                                                                                                                    
  273.      , DECODE( '20110728'                                                
  274.              , ( SELECT TO_CHAR( LAST_DAY( TO_DATE( '20110728', 'YYYYMMDD' ) ), 'YYYYMMDD' ) FROM dual ), 0       
  275.              ,   adj_credit_int_lv1_amt                      
  276.                + adj_credit_int_lv2_amt                            
  277.                - adj_debit_int_lv1_amt                               
  278.                - adj_debit_int_lv2_amt )                                  
  279.           AS interest                                               
  280. FROM   acc_pos_int_tbl                                          
  281. WHERE  SUBSTR( business_date, 1, 6 ) = SUBSTR( '20110728', 1, 6 ) AND business_date <= '20110728';       
  282.                                                                                                                                   
  283. -->改进的办法              
  284. SELECT acc_num                                             
  285.      , curr_cd                                           
  286.      , DECODE( '20110728'                                  
  287.              , ( SELECT TO_CHAR( LAST_DAY( TO_DATE( '20110728', 'YYYYMMDD' ) ), 'YYYYMMDD' ) FROM dual ), 0    
  288.              ,   adj_credit_int_lv1_amt                      
  289.                + adj_credit_int_lv2_amt                         
  290.                - adj_debit_int_lv1_amt                            
  291.                - adj_debit_int_lv2_amt )                             
  292.           AS interest                                       
  293. FROM   acc_pos_int_tbl acc_pos_int_tbl                                               
  294. WHERE  business_date >= TO_CHAR( LAST_DAY( ADD_MONTHS( TO_DATE( '20110728', 'yyyymmdd' ), -1 ) )    
  295.                                 + 1, 'yyyymmdd' )                        
  296.        AND business_date <= '20110728';                   
  297.                                                                                                                                   
  298. -->面下的例子虽然没有应用函数,但字符串连接同样致导索引效失                                                                       
  299. -->低效:                       
  300. SELECT account_name, amount                                                                                                       
  301. FROM   transaction                                                                                                                
  302. WHERE  account_name                                                                                                               
  303.        || account_type = 'AMEXA';                                                                                                 
  304.                                                                                                                                   
  305. -->高效:                          
  306. SELECT account_name, amount                                                                                                       
  307. FROM   transaction                                                                                                                
  308. WHERE  account_name = 'AMEX' AND account_type = 'A';                                                                              
  309.                                                                                                                                   
  310. 5) 比较不匹配的数据类型                                                                                                           
  311. -->面下的查询中business_date列上存在索引,且为字符型,这类                                                                          
  312. -->低效:                                     
  313. SELECT *                                                                                                                          
  314. FROM   acc_pos_int_tbl                                                                                                            
  315. WHERE  business_date = 20090201;                                                                                                 
  316.                                                                                                                                   
  317. Execution Plan                                                                                                                    
  318. ----------------------------------------------------------        
  319. Plan hash value: 2335235465                      
  320.                                                  
  321. -------------------------------------------------------------------------------------                 
  322. | Id  | Operation         | Name            | Rows  | Bytes | Cost (%CPU)| Time     |                         
  323. -------------------------------------------------------------------------------------                        
  324. |   0 | SELECT STATEMENT  |                 | 37516 |  2857K|   106K  (1)| 00:21:17 |                      
  325. |*  1 |  TABLE ACCESS FULL| ACC_POS_INT_TBL | 37516 |  2857K|   106K  (1)| 00:21:17 |                 
  326. -------------------------------------------------------------------------------------                    
  327.                                                                                                                                   
  328. Predicate Information (identified by operation id):                          
  329. ---------------------------------------------------         
  330.      1 - filter(TO_NUMBER("BUSINESS_DATE")=20090201)    -->这里可以看到生产了类型转换             
  331.                                                                                                                                   
  332. -->高效:                                        
  333. SELECT *                                                                                                                          
  334. FROM   acc_pos_int_tbl                                                                                                            
  335. WHERE  business_date = '20090201'                                                                                                 
  336.                                                                                                                                   
  337. 6) 索引列上应用 NULL 值             
  338.     IS NULL和IS NOT NULL会限制索引的应用,因为数据中没有值等于NULL值,即便是NULL值也不等于NULL值.且NULL值不存储在于索引之中    
  339. 因此应尽可能免避在索引类上应用NULL值                                                                                              
  340.                                                                                                                                   
  341. SELECT acc_num                                                                                                                    
  342.      , pl_cd                                                                                                                      
  343.      , order_qty                                                                                                                  
  344.      , trade_date                                                                                                                 
  345. FROM   trade_client_tbl                                                                                                           
  346. WHERE  input_date IS NOT NULL;                                                                                                    
  347.                                                                                                                                   
  348. Execution Plan                                              
  349. ----------------------------------------------------------                          
  350. Plan hash value: 901462645                                        
  351. --------------------------------------------------------------------------------------                   
  352. | Id  | Operation         | Name             | Rows  | Bytes | Cost (%CPU)| Time     |                     
  353. --------------------------------------------------------------------------------------                   
  354. |   0 | SELECT STATEMENT  |                  |     1 |    44 |    15   (0)| 00:00:01 |                
  355. |*  1 |  TABLE ACCESS FULL| TRADE_CLIENT_TBL |     1 |    44 |    15   (0)| 00:00:01 |                     
  356. --------------------------------------------------------------------------------------                   
  357.                                             
  358. alter table trade_client_tbl modify (input_date not null);            
  359.                                                                      
  360. 不推荐应用的查询方法                                        
  361. SELECT * FROM table_name WHERE col IS NOT NULL                        
  362.                                                                   
  363. SELECT * FROM table_name WHERE col IS NULL                                 
  364.                                                                                                                                   
  365. 推荐应用的方法                        
  366. SELECT * FROM table_name WHERE col >= 0 --尽可能的应用 =, >=, <=, like 等算运符       
  367. -->Author: Robinson Cheng                

          

1) 免避基于索引列的盘算                                                                                                         
where 句子中的谓词上存在索引,而此时基于该列的盘算将使得索引效失

-->低效:
SELECT employee_id, first_name
FROM employees
WHERE employee_id + 10 > 150; -->索引列上应用了盘算,因此索引效失,走全表描扫方法

-->高效:
SELECT employee_id, first_name
FROM employees
WHERE employee_id > 160; -->走索引范围描扫方法

例外况情
上述规则不适于用SQL中的MIN和MAX函数
hr@CNMMBO> SELECT MAX( employee_id ) max_id
2 FROM employees
3 WHERE employee_id
4 + 10 > 150;

1 row selected.

Execution Plan
----------------------------------------------------------
Plan hash value: 1481384439
---------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 4 | 1 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 4 | | |
| 2 | FIRST ROW | | 5 | 20 | 1 (0)| 00:00:01 |
|* 3 | INDEX FULL SCAN (MIN/MAX)| EMP_EMP_ID_PK | 5 | 20 | 1 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------

2) 免避在索引列上应用NOT算运或不等于算运(<>,!=)
常通,我们要免避在索引列上应用NOT或<>,两者会生产在和在索引列上应用函数雷同的影响。 当ORACLE遇到NOT或不等算运时,他就会停止
应用索引转而执行全表描扫。

-->低效:
SELECT *
FROM emp
WHERE NOT ( deptno = 20 ); -->现实上NOT ( deptno = 20 )等同于deptno <> 20,即deptno <>同样会限制索引

-->高效:
SELECT *
FROM emp
WHERE deptno > 20 OR deptno < 20;
-->尽管此方法可以替换且实现上述结果,但依然走全表描扫,如果是单纯的 > 或 < 算运,则此时为索引范围描扫

要需注意的是,在某些时候, ORACLE优化器会自动将NOT转化成相对应的关系作操符
其次如果是下列算运符停止NOT算运,依然有可能择选走索引, 仅仅除了NOT = 之外,因为 NOT = 等价于 <>

“NOT >” to <=
“NOT >=” to <
“NOT <” to >=
“NOT <=” to >

来看一个现实的例子
hr@CNMMBO> SELECT *
2 FROM employees
3 where not employee_id<100; -->索引列上应用了not,但是该查询返回了有所的录记,即107条,因此此时择选走全表描扫

107 rows selected.

Execution Plan
----------------------------------------------------------
Plan hash value: 1445457117
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 107 | 7276 | 3 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| EMPLOYEES | 107 | 7276 | 3 (0)| 00:00:01 | -->执行计划中应用了走全表描扫方法
-------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter("EMPLOYEE_ID">=100) -->查看这里的谓词息信被自动转换为 >= 算运符

hr@CNMMBO> SELECT *
2 FROM employees
3 where not employee_id<140; -->此例与上面的语句雷同,仅仅是查询范围不同返回67条录记,而此时择选了索引范围描扫

67 rows selected.

Execution Plan
----------------------------------------------------------
Plan hash value: 603312277

---------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 68 | 4624 | 3 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| EMPLOYEES | 68 | 4624 | 3 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | EMP_EMP_ID_PK | 68 | | 1 (0)| 00:00:01 | -->索引范围描扫方法
---------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("EMPLOYEE_ID">=140)

3) 用UNION 替换OR(适于用索引列)
常通况情下,应用UNION 替换WHERE句子中的OR将会起到较好的效果.基于索引列应用OR使得优化器倾向于应用全表描扫,而不是描扫索引.
注意,以上规则仅适于用多个索引列有效。 如果有column没有被索引, 查询效率可能会因为你没有择选OR而降低。
-->低效:
SELECT deptno, dname
FROM dept
WHERE loc = 'DALLAS' OR deptno = 20;

-->高效:
SELECT deptno, dname
FROM dept
WHERE loc = 'DALLAS'
UNION
SELECT deptno, dname
FROM dept
WHERE deptno = 30

-->经试测,由于数据量较少,此时where句子中的谓词上都存在索引列时,两者性能相当.
-->假定where句子中存在两列
scott@CNMMBO> create table t6 as select object_id,owner,object_name from dba_objects where owner='SYS' and rownum<1001;

scott@CNMMBO> insert into t6 select object_id,owner,object_name from dba_objects where owner='SCOTT' and rownum<6;

scott@CNMMBO> create index i_t6_object_id on t6(object_id);

scott@CNMMBO> create index i_t6_owner on t6(owner);

scott@CNMMBO> insert into t6 select object_id,owner,object_name from dba_objects where owner='SYSTEM' and rownum<=300;

scott@CNMMBO> commit;

scott@CNMMBO> exec dbms_stats.gather_table_stats('SCOTT','T6',cascade=>true);

scott@CNMMBO> select owner,count(*) from t6 group by owner;

OWNER COUNT(*)
-------------------- ----------
SCOTT 5
SYSTEM 300
SYS 1000

scott@CNMMBO> select * from t6 where owner='SCOTT' and rownum<2;

OBJECT_ID OWNER OBJECT_NAME
---------- -------------------- --------------------
69450 SCOTT T_TEST

scott@CNMMBO> select * from t6 where object_id=69450 or owner='SYSTEM';

301 rows selected.

Execution Plan
----------------------------------------------------------
Plan hash value: 238853296
-----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 300 | 7200 | 5 (0)| 00:00:01 |
| 1 | CONCATENATION | | | | | |
| 2 | TABLE ACCESS BY INDEX ROWID| T6 | 1 | 24 | 2 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | I_T6_OBJECT_ID | 1 | | 1 (0)| 00:00:01 |
|* 4 | TABLE ACCESS BY INDEX ROWID| T6 | 299 | 7176 | 3 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | I_T6_OWNER | 300 | | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("OBJECT_ID"=69450)
4 - filter(LNNVL("OBJECT_ID"=69450))
5 - access("OWNER"='SYSTEM')

Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
46 consistent gets
0 physical reads
0 redo size
11383 bytes sent via SQL*Net to client
712 bytes received via SQL*Net from client
22 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
301 rows processed

scott@CNMMBO> select * from t6 where owner='SYSTEM' or object_id=69450;

301 rows selected.

Execution Plan
----------------------------------------------------------
Plan hash value: 238853296
-----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 300 | 7200 | 5 (0)| 00:00:01 |
| 1 | CONCATENATION | | | | | |
| 2 | TABLE ACCESS BY INDEX ROWID| T6 | 1 | 24 | 2 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | I_T6_OBJECT_ID | 1 | | 1 (0)| 00:00:01 |
|* 4 | TABLE ACCESS BY INDEX ROWID| T6 | 299 | 7176 | 3 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | I_T6_OWNER | 300 | | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("OBJECT_ID"=69450)
4 - filter(LNNVL("OBJECT_ID"=69450))
5 - access("OWNER"='SYSTEM')

Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
46 consistent gets
0 physical reads
0 redo size
11383 bytes sent via SQL*Net to client
712 bytes received via SQL*Net from client
22 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
301 rows processed

scott@CNMMBO> select * from t6
2 where object_id=69450
3 union
4 select * from t6
5 where owner='SYSTEM';

301 rows selected.

Execution Plan
----------------------------------------------------------
Plan hash value: 370530636
------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 301 | 7224 | 7 (72)| 00:00:01 |
| 1 | SORT UNIQUE | | 301 | 7224 | 7 (72)| 00:00:01 |
| 2 | UNION-ALL | | | | | |
| 3 | TABLE ACCESS BY INDEX ROWID| T6 | 1 | 24 | 2 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | I_T6_OBJECT_ID | 1 | | 1 (0)| 00:00:01 |
| 5 | TABLE ACCESS BY INDEX ROWID| T6 | 300 | 7200 | 3 (0)| 00:00:01 |
|* 6 | INDEX RANGE SCAN | I_T6_OWNER | 300 | | 1 (0)| 00:00:01 |
------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("OBJECT_ID"=69450)
6 - access("OWNER"='SYSTEM')

Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
7 consistent gets
0 physical reads
0 redo size
11383 bytes sent via SQL*Net to client
712 bytes received via SQL*Net from client
22 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
301 rows processed

-->从上面的统计息信可知,consistent gets由46下降为7,故当where句子中谓词上存在索引时,应用union替换or更高效
-->即使当列object_id与owner上不存在索引时,应用union仍然比or更高效(在Oracle 10g R2与Oracle 11g R2试测)

4) 免避索引列上应用函数
-->面下是一个来自现实生产境环的例子
-->表acc_pos_int_tbl上business_date列存在索引,由于应用了SUBSTR函数,此时索引效失,应用全表描扫
SELECT acc_num
, curr_cd
, DECODE( '20110728'
, ( SELECT TO_CHAR( LAST_DAY( TO_DATE( '20110728', 'YYYYMMDD' ) ), 'YYYYMMDD' ) FROM dual ), 0
, adj_credit_int_lv1_amt
+ adj_credit_int_lv2_amt
- adj_debit_int_lv1_amt
- adj_debit_int_lv2_amt )
AS interest
FROM acc_pos_int_tbl
WHERE SUBSTR( business_date, 1, 6 ) = SUBSTR( '20110728', 1, 6 ) AND business_date <= '20110728';

-->改进的办法
SELECT acc_num
, curr_cd
, DECODE( '20110728'
, ( SELECT TO_CHAR( LAST_DAY( TO_DATE( '20110728', 'YYYYMMDD' ) ), 'YYYYMMDD' ) FROM dual ), 0
, adj_credit_int_lv1_amt
+ adj_credit_int_lv2_amt
- adj_debit_int_lv1_amt
- adj_debit_int_lv2_amt )
AS interest
FROM acc_pos_int_tbl acc_pos_int_tbl
WHERE business_date >= TO_CHAR( LAST_DAY( ADD_MONTHS( TO_DATE( '20110728', 'yyyymmdd' ), -1 ) )
+ 1, 'yyyymmdd' )
AND business_date <= '20110728';

-->面下的例子虽然没有应用函数,但字符串连接同样致导索引效失
-->低效:
SELECT account_name, amount
FROM transaction
WHERE account_name
|| account_type = 'AMEXA';

-->高效:
SELECT account_name, amount
FROM transaction
WHERE account_name = 'AMEX' AND account_type = 'A';

5) 比较不匹配的数据类型
-->面下的查询中business_date列上存在索引,且为字符型,这类
-->低效:
SELECT *
FROM acc_pos_int_tbl
WHERE business_date = 20090201;

Execution Plan
----------------------------------------------------------
Plan hash value: 2335235465

-------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 37516 | 2857K| 106K (1)| 00:21:17 |
|* 1 | TABLE ACCESS FULL| ACC_POS_INT_TBL | 37516 | 2857K| 106K (1)| 00:21:17 |
-------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(TO_NUMBER("BUSINESS_DATE")=20090201) -->这里可以看到生产了类型转换

-->高效:
SELECT *
FROM acc_pos_int_tbl
WHERE business_date = '20090201'

6) 索引列上应用 NULL 值
IS NULL和IS NOT NULL会限制索引的应用,因为数据中没有值等于NULL值,即便是NULL值也不等于NULL值.且NULL值不存储在于索引之中
因此应尽可能免避在索引类上应用NULL值

SELECT acc_num
, pl_cd
, order_qty
, trade_date
FROM trade_client_tbl
WHERE input_date IS NOT NULL;

Execution Plan
----------------------------------------------------------
Plan hash value: 901462645
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 44 | 15 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| TRADE_CLIENT_TBL | 1 | 44 | 15 (0)| 00:00:01 |
--------------------------------------------------------------------------------------

alter table trade_client_tbl modify (input_date not null);

不推荐应用的查询方法
SELECT * FROM table_name WHERE col IS NOT NULL

SELECT * FROM table_name WHERE col IS NULL

推荐应用的方法
SELECT * FROM table_name WHERE col >= 0 --尽可能的应用 =, >=, <=, like 等算运符
-->Author: Robinson Cheng


    三、结总

1、尽可能最小化基表数据以及中间结果集(通过过滤件条免避后续生产不必要的盘算与聚合)

2、为where句子中的谓词息信提供最好的拜访路径(rowid拜访,索引拜访)

3、应用理合的SQL写法来免避过多的Oracle外部销开以高提性能

4、理合的应用提示以高提表之间的连接来高提连接效率(如免避迪卡尔集,将不理合的嵌套连接改为hash连接等)