文章内容非常长,大家可以有空就看一点。
1. 选用适合的ORACLE优化器
b.COST (基于成本)
c.CHOOSE (选择性)
如RULE,COST,CHOOSE,ALL_ROWS,FIRST_ROWS,你当然也在SQL句级或是会话(session)级对其进行覆盖。
如果Table已经被Analyze过,优化器模式将自动成为CBO,反之,数据库将采用RULE形式的优化器。
2. 访问Table的方式
3. 共享SQL语句
这块位于系统全局区域SGA(System GlobalArea)的共享池(Shared Buffer Pool)中的内存可以被所有的数据库用户共享。
因此,当你执行一个SQL语句(有时被称为一个光标)时,如果它和之前的执行过的语句完全相同,ORACLE就能很快获得已经被解析的语句以及最好的执行路径。
ORACLE的这个功能大大地提高了SQL的执行性能并节省了内存的使用。可惜的是ORACLE只对简单的表提供高速缓冲(cache buffering),这个功能并不适用于多表连接查询。
数据库管理员必须在init.ora中为这个区域设置合适的参数,当这个内存区域越大,就可以保留更多的语句,当然被共享的可能性也就越大了。
这里需要注明的是,ORACLE对两者采取的是一种严格匹配,要达成共享,SQL语句必须完全相同(包括空格,换行等).
SELECT* FROM EMP;
和下列每一个都不同
SELECT* from EMP;Select* From Emp;SELECT * FROM EMP;
Jacksal_limit private synonymWork_city public synonymPlant_detail public synonymJillsal_limit private synonymWork_city public synonymPlant_detailtable owner
a.
selectpin ,name from people where pin = :blk1.pin;selectpin ,name from people where pin = :blk1.pin;
b.
selectpin ,name from people where pin = :blk1.ot_ind;selectpin ,name from people where pin = :blk1.ov_ind;
4. 选择最有效率的表名顺序(只在基于规则的优化器中有效)
在FROM子句中包含多个表的情况下,你必须选择记录条数最少的表作为基础表.
当ORACLE处理多个表时,会运用排序及合并的方式连接它们.首先,扫描第一个表(FROM子句中最后的那个表)并对记录进行派序,然后扫描第二个表(FROM子句中最后第二个表),最后将所有从第二个表中检索出的记录与第一个表中合适记录进行合并.
select count(*) from tab1,tab2 执行时间0.96秒
select count(*) from tab2,tab1 执行时间26.09秒
如果有3个以上的表连接查询,那就需要选择交叉表(inter section table)作为基础表,交叉表是指那个被其它表所引用的表.
SELECT *FROM LOCATION L, CATEGORY C, EMP EWHERE E.EMP_NO BETWEEN 1000 AND 2000AND E.CAT_NO = C.CAT_NOAND E.LOCN = L.LOCN
SELECT *FROM EMP E, LOCATION L ,CATEGORY CWHERE E.CAT_NO = C.CAT_NOAND E.LOCN = L.LOCNAND E.EMP_NO BETWEEN 1000 AND 2000
5. WHERE子句中的连接顺序.
SELECT ...FROM EMP EWHERE SAL > 50000AND JOB = 'MANAGER'AND 25 < (SELECT COUNT(*) FROM EMP WHERE MGR=E.EMPNO);
SELECT ...FROM EMP EWHERE 25 < (SELECT COUNT(*) FROM EMP WHERE MGR=E.EMPNO) AND SAL > 50000 AND JOB = 'MANAGER';
6. SELECT子句中避免使用 '*'
实际上,ORACLE在解析的过程中,会将'*' 依次转换成所有的列名,这个工作是通过查询数据字典完成的,这意味着将耗费更多的时间.
7. 减少访问数据库的次数
由此可见,减少访问数据库的次数,就能实际上减少ORACLE的工作量.
SELECT EMP_NAME ,SALARY ,GRADEFROM EMPWHERE EMP_NO= 342;
方法2 (次低效)
DECLARE CURSOR C1 (E_NO NUMBER) ISSELECT EMP_NAME, SALARY, GRADEFROM EMPWHERE EMP_NO = E_NO;BEGIN OPEN C1(342);FETCH C1 INTO…,..,.. ;CLOSE C1;END;
SELECT A.EMP_NAME ,A.SALARY ,A.GRADE,B.EMP_NAME,B.SALARY ,B.GRADEFROM EMP A,EMP BWHERE A.EMP_NO = 342AND B.EMP_NO = 291;
8. 使用DECODE函数来减少处理时间
SELECT COUNT(*),SUM(SAL)FROM EMPWHERE DEPT_NO = 0020AND ENAME LIKE 'SMITH%';SELECT COUNT(*),SUM(SAL)FROM EMPWHERE DEPT_NO = 0030AND ENAME LIKE 'SMITH%'
SELECT COUNT(DECODE(DEPT_NO,0020,'X',NULL))D0020_COUNT,COUNT(DECODE(DEPT_NO,0030,'X',NULL))D0030_COUNT,SUM(DECODE(DEPT_NO,0020,SAL,NULL)) D0020_SAL,SUM(DECODE(DEPT_NO,0030,SAL,NULL)) D0030_SALFROM EMPWHERE ENAME LIKE 'SMITH%';
9. 整合简单,无关联的数据库访问
SELECT NAMEFROM EMPWHERE EMP_NO = 1234;SELECT NAMEFROM DPTWHERE DPT_NO = 10 ;SELECT NAMEFROM CATWHERE CAT_TYPE = 'RD';
SELECT E.NAME ,D.NAME ,C.NAMEFROMCAT C, DPT D, EMP E, DUAL XWHERE 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(+) = 1234AND D.DEPT_NO(+) = 10AND C.CAT_TYPE(+) = 'RD';
10. 删除重复记录
DELETEFROM EMP EWHERE E.ROWID > (SELECT MIN(X.ROWID)FROM EMP X WHERE X.EMP_NO = E.EMP_NO);
11. 用TRUNCATE替代DELETE
如果你没有COMMIT事务,ORACLE会将数据恢复到删除之前的状态(准确地说是恢复到执行删除命令之前的状况)
12. 尽量多使用COMMIT
13. 计算记录条数
14. 用Where子句替换HAVING子句
HAVING 只会在检索出所有记录之后才对结果集进行过滤. 这个处理需要排序,总计等操作.
如果能通过WHERE子句限制记录的数目,那就能减少这方面的开销.
SELECT REGION,AVG(LOG_SIZE)FROM LOCATIONGROUP BY REGIONHAVING REGION REGION != 'SYDNEY'AND REGION != 'PERTH'
SELECT REGION,AVG(LOG_SIZE)FROM LOCATIONWHERE REGION REGION != 'SYDNEY'AND REGION != 'PERTH'GROUP BY REGION
15. 减少对表的查询
SELECT TAB_NAMEFROM TABLESWHERE TAB_NAME = ( SELECT TAB_NAMEFROM TAB_COLUMNSWHERE VERSION = 604)AND DB_VER= ( SELECT DB_VERFROM TAB_COLUMNSWHERE VERSION = 604)
SELECT TAB_NAMEFROM TABLESWHERE (TAB_NAME, DB_VER) = ( SELECT TAB_NAME, DB_VER)FROM TAB_COLUMNSWHERE VERSION =604)
UPDATE EMPSETEMP_CAT = (SELECT MAX(CATEGORY) FROM EMP_CATEGORIES),SAL_RANGE= (SELECT MAX(SAL_RANGE) FROM EMP_CATEGORIES)WHERE EMP_DEPT = 0020;
UPDAT EEMPSET (EMP_CAT,SAL_RANGE) = (SELECT MAX(CATEGORY) ,MAX(SAL_RANGE)FROM EMP_CATEGORIES)WHERE EMP_DEPT = 0020;
16. 通过内部函数提高SQL效率
SELECTH.EMPNO,E.ENAME,H.HIST_TYPE,T.TYPE_DESC,COUNT(*)FROM HISTORY_TYPE T,EMP E,EMP_HISTORY HWHEREH.EMPNO = E.EMPNOAND H.HIST_TYPE = T.HIST_TYPEGROUP BYH.EMPNO,E.ENAME,H.HIST_TYPE,T.TYPE_DESC;
FUNCTION LOOKUP_HIST_TYPE(TYP IN NUMBER) RETURN VARCHAR2ASTDESC VARCHAR2(30);CURSOR C1 ISSELECT TYPE_DESCFROM HISTORY_TYPEWHERE HIST_TYPE = TYP;BEGINOPEN C1;FETCH C1 INTO TDESC;CLOSE C1;RETURN (NVL(TDESC,'?'));END;FUNCTION LOOKUP_EMP(EMP IN NUMBER) RETURN VARCHAR2ASENAME VARCHAR2(30);CURSOR C1 ISSELECT ENAMEFROM EMPWHERE EMPNO=EMP;BEGINOPEN C1;FETCH C1 INTO ENAME;CLOSE C1;RETURN (NVL(ENAME,'?'));END;SELECT H.EMPNO,LOOKUP_EMP(H.EMPNO),H.HIST_TYPE,LOOKUP_HIST_TYPE(H.HIST_TYPE),COUNT(*)FROM EMP_HISTORY HGROUP BY H.EMPNO ,H.HIST_TYPE;
17. 使用表的别名(Alias)
18. 用EXISTS替代IN
SELECT *FROM EMP (基础表)WHERE EMPNO > 0AND DEPTNO IN (SELECT DEPTNOFROM DEPTWHERE LOC = 'MELB')
高效:
SELECT *FROM EMP (基础表)WHERE EMPNO > 0AND EXISTS (SELECT 'X'FROM DEPTWHERE DEPT.DEPTNO = EMP.DEPTNOAND LOC = 'MELB')
19. 用NOT EXISTS替代NOT IN
无论在哪种情况下,NOT IN都是最低效的 (因为它对子查询中的表执行了一个全表遍历).
为了避免使用NOT IN ,我们可以把它改写成外连接(Outer Joins)或NOT EXISTS.
SELECT …FROM EMPWHERE DEPT_NO NOT IN (SELECT DEPT_NOFROM DEPTWHERE DEPT_CAT='A');
为了提高效率.改写为:
(方法一: 高效)
SELECT….FROM EMP A,DEPT BWHERE A.DEPT_NO = B.DEPT(+)AND B.DEPT_NO IS NULLAND B.DEPT_CAT(+) = 'A'
(方法二: 最高效)
SELECT …FROM EMP EWHERE NOT EXISTS (SELECT 'X'FROM DEPT DWHERE D.DEPT_NO = E.DEPT_NOAND DEPT_CAT = 'A');
20. 用表连接替换EXISTS
SELECT ENAMEFROM EMP EWHERE EXISTS (SELECT 'X'FROM DEPTWHERE DEPT_NO = E.DEPT_NOAND DEPT_CAT = 'A');(更高效)SELECT ENAMEFROM DEPT D,EMP EWHERE E.DEPT_NO = D.DEPT_NOAND DEPT_CAT = 'A' ;
21. 用EXISTS替换DISTINCT
SELECT DISTINCT DEPT_NO,DEPT_NAMEFROM DEPT D,EMP EWHERE D.DEPT_NO = E.DEPT_NO
高效:
SELECT DEPT_NO,DEPT_NAMEFROM DEPT DWHERE EXISTS ( SELECT 'X'FROM EMP EWHERE E.DEPT_NO =D.DEPT_NO);
EXISTS 使查询更为迅速,因为RDBMS核心模块将在子查询的条件一旦满足后,立刻返回结果.
22. 识别'低效执行'的SQL语句
SELECT EXECUTIONS ,DISK_READS,BUFFER_GETS,ROUND((BUFFER_GETS-DISK_READS)/BUFFER_GETS,2) Hit_radio,ROUND(DISK_READS/EXECUTIONS,2) Reads_per_run,SQL_TEXTFROM V$SQLAREAWHERE EXECUTIONS>0AND BUFFER_GETS > 0AND (BUFFER_GETS-DISK_READS)/BUFFER_GETS < 0.8ORDERBY 4 DESC;
虽然目前各种关于SQL优化的图形化工具层出不穷,但是写出自己的SQL工具来解决问题始终是一个最好的方法
23. 使用TKPROF 工具来查询SQL性能状态
例如解析次数.执行次数,CPU使用时间等.这些数据将可以用来优化你的系统.
ALTER SESSION SET SQL_TRACE TRUE
USER_DUMP_DEST参数说明了生成跟踪文件的目录
24. 用EXPLAIN PLAN 分析SQL语句
通过分析,我们就可以知道ORACLE是怎么样连接表,使用什么方式扫描表(索引扫描或全表扫描)以及使用到的索引名称.
你需要按照从里到外,从上到下的次序解读分析的结果. EXPLAIN PLAN分析的结果是用缩进的格式排列的,最内部的操作将被最先解读,如果两个操作处于同一层中,带有最小操作号的将被首先执行.
SQL> @D:\oracle\ora90\rdbms\admin\utlxplan.sqlSQL> @D:\oracle\ora90\sqlplus\admin\plustrce.sqlSQL>list1 SELECT *2 FROMdept,emp3* WHERE emp.deptno = dept.deptnoSQL> set autotrace traceonly// SQL>set timing on 显示执行时间// SQL>set autorace on 显示执行计划// SQL>set autotrace traceonly只显示执行计划即不显示查询出来的数据SQL>/14rows selected.ExecutionPlan----------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE1 0 NESTED LOOPS2 1 TABLE ACCESS (FULL) OF 'EMP'3 1 TABLE ACCESS (BY INDEX ROWID)OF 'DEPT'4 3 INDEX (UNIQUE SCAN) OF'PK_DEPT' (UNIQUE)Statistics----------------------------------------------------------0 recursive calls2 db block gets30 consistent gets0 physical reads0 redo size2598 bytes sent via SQL*Net to client503 bytes received via SQL*Net from client2 SQL*Net roundtrips to/from client0 sorts (memory)0 sorts (disk)14 rows processed
1.TABLEACCESS (FULL) OF 'EMP'2.INDEX(UNIQUE SCAN) OF 'PK_DEPT' (UNIQUE)3.TABLEACCESS (BY INDEX ROWID) OF 'DEPT'4.NESTEDLOOPS (JOINING 1 AND 3)
25. 用索引提高效率
实际上,ORACLE使用了一个复杂的自平衡B-tree结构. 通常,通过索引查询数据比全表扫描要快.
当ORACLE找出执行查询和Update语句的最佳路径时,ORACLE优化器将使用索引. 同样在联结多个表时使用索引也可以提高效率.
另一个使用索引的好处是,它提供了主键(primary key)的唯一性验证.除了那些LONG或LONG RAW数据类型,你可以索引几乎所有的列.
通常,在大型表中使用索引特别有效. 当然,你也会发现,在扫描小表时,使用索引同样能提高效率.
这意味着每条记录的INSERT ,DELETE,UPDATE将为此多付出4 ,5 次的磁盘I/O .
因为索引需要额外的存储空间和处理,那些不必要的索引反而会使查询反应时间变慢
ALTER INDEX <INDEXNAME> REBUILD <TABLESPACENAME>
26. 索引的操作
建立在LODGING列上的唯一性索引LODGING_PK和建立在MANAGER列上的非唯一性索引LODGING$MANAGER.
SELECT * FROM LODGING WHERE LODGING = 'ROSE HILL';
在内部 ,上述SQL将被分成两步执行,首先 ,LODGING_PK 索引将通过索引唯一扫描的方式被访问,获得相对应的ROWID,通过ROWID访问表的方式 执行下一步检索.
SELECT LODGING FROM LODGING WHERE LODGING = 'ROSE HILL';
SELECT LODGING FROM LODGING WHERE LODGING LIKE 'M%';
SELECT LODGING FROM LODGING WHERE MANAGER = 'BILL GATES';
由于LODGING$MANAGER是一个非唯一性的索引,数据库不能对它执行索引唯一扫描.
SELECT LODGING FROM LODGING WHERE MANAGER LIKE '%HANMAN';
27. 基础表的选择
SELECT A.NAME ,B.MANAGERFROM WORKER A,LODGING BWHERE A.LODGING = B.LODING;
28. 多个平等的索引
然而这个规则只有当WHERE子句中索引列和常量比较才有效.如果索引列和其它表的索引类相比较. 这种子句在优化器中的等级是非常低的.
SELECT ENAME,FROM EMP WHERE DEPT_NO = 20 AND EMP_CAT = 'A';
TABLEACCESS BY ROWID ON EMPAND-EQUALINDEX RANGE SCAN ON DEPT_IDXINDEX RANGE SCAN ON CAT_IDX
29. 等式比较和范围比较
SELECT ENAMEFROM EMPWHERE DEPTNO> 20AND EMP_CAT ='A';
TABLE ACCESSBY ROWID ON EMPINDEX RANGESCAN ON CAT_IDX
30. 不明确的索引等级
SELECT ENAM EFROM EMPWHERE DEPTNO > 20AND EMP_CAT > 'A';
TABLEACCESS BY ROWID ON EMPINDEX RANGE SCAN ON DEPT_IDX
SQL>select index_name,uniqueness from user_indexes where table_name = 'EMP';INDEX_NAME UNIQUENES---------------------------------------EMPNO UNIQUEEMPTYPE NONUNIQUESQL>select * from emp where empno >= 2 and emp_type = 'A' ;norows selectedExecutionPlan----------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE1 0 TABLE ACCESS (BY INDEX ROWID)OF 'EMP'2 1 INDEX (RANGE SCAN) OF 'EMPTYPE' (NON-UNIQUE)
31. 强制索引失效
SELECT ENAMEFROM EMPWHERE EMPNO = 7935AND DEPTNO + 0 = 10 *DEPTNO上的索引将失效*/AND EMP_TYPE || '' = 'A' *EMP_TYPE上的索引将失效*/
SELECT ENAME FROM EMP WHERE EMP_TYPE = 'A' AND EMP_CLASS = 'X';
如果,一段时间以后,另一个非唯一性建立在EMP_CLASS上,优化器必须对两个索引进行选择,在通常情况下,优化器将使用两个索引并在他们的结果集合上执行排序及合并.
然而,如果其中一个索引(EMP_TYPE)接近于唯一性而另一个索引(EMP_CLASS)上有几千个重复的值.
排序及合并就会成为一种不必要的负担. 在这种情况下,你希望使优化器屏蔽掉EMP_CLASS索引.
SELECT ENAME FROMEMP WHERE EMP_TYPE = 'A' AND EMP_CLASS||'' = 'X';
32. 避免在索引列上使用计算
SELECT …FROM DEPTWHERE SAL * 12 > 25000;
SELECT …FROM DEPTWHERE SAL > 25000/12;
33. 自动选择索引
SELECT ENAME FROM EMP WHERE EMPNO = 2326 AND DEPTNO = 20 ;
TABLEACCESS BY ROWID ON EMPINDEXUNIQUE SCAN ON EMP_NO_IDX
34. 避免在索引列上使用NOT
SELECT … FROM DEPT WHERE NOT DEPT_CODE = 0
SELECT … FROM DEPT WHERE DEPT_CODE > 0;
NOT > to <=NOT >= to <NOT < to >=NOT <= to >
35. 用UNION替换OR (适用于索引列)
注意,以上规则只针对多个索引列有效. 如果有column没有被索引,查询效率可能会因为你没有选择OR而降低.
SELECTLOC_ID ,LOC_DESC ,REGIONFROM LOCATIONWHERE LOC_ID = 10UNIONSELECT LOC_ID ,LOC_DESC ,REGIONFROM LOCATIONWHERE REGION = "MELBOURNE"
低效:
SELECT LOC_ID ,LOC_DESC ,REGIONFROM LOCATIONWHERE LOC_ID = 10 OR REGION = "MELBOURNE"
WHEREKEY1 = 10 (返回最少记录)ORKEY2 = 20 (返回最多记录)ORACLE内部将以上转换为WHEREKEY1 = 10 AND((NOTKEY1 = 10) ANDKEY2 = 20)
SQL>select * from unionvsor *1st test*/2 where a = 1003 or b = 1;1003rows selected.ExecutionPlan----------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE1 0 CONCATENATION2 1 TABLE ACCESS (BY INDEXROWID) OF 'UNIONVSOR'3 2 INDEX (RANGE SCAN) OF 'UB'(NON-UNIQUE)4 1 TABLE ACCESS (BY INDEXROWID) OF 'UNIONVSOR'5 4 INDEX (RANGE SCAN) OF 'UA'(NON-UNIQUE)Statistics----------------------------------------------------------0 recursive calls0 db block gets144 consistent gets0 physical reads0 redo size63749 bytes sent via SQL*Net to client7751 bytes received via SQL*Net from client68 SQL*Net roundtrips to/from client0 sorts (memory)0 sorts (disk)1003 rows processedSQL>select * from unionvsor *2nd test*/2 where b = 1 or a = 1003 ;1003rows selected.ExecutionPlan----------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE1 0 CONCATENATION2 1 TABLE ACCESS (BY INDEXROWID) OF 'UNIONVSOR'3 2 INDEX (RANGE SCAN) OF 'UA'(NON-UNIQUE)4 1 TABLE ACCESS (BY INDEXROWID) OF 'UNIONVSOR'5 4 INDEX (RANGE SCAN) OF 'UB'(NON-UNIQUE)Statistics----------------------------------------------------------0 recursive calls0 dbblock gets143 consistent gets0 physical reads0 redo size63749 bytes sent via SQL*Net to client7751 bytes received via SQL*Net from client68 SQL*Net roundtrips to/from client0 sorts (memory)0 sorts (disk)1003 rows processedSQL>select * from unionvsor *3rd test*/2 where a = 10033 union4 select * from unionvsor5 where b = 1;1003rows selected.ExecutionPlan----------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE1 0 SORT (UNIQUE)2 1 UNION-ALL3 2 TABLE ACCESS (BY INDEXROWID) OF 'UNIONVSOR'4 3 INDEX (RANGE SCAN) OF'UA' (NON-UNIQUE)5 2 TABLE ACCESS (BY INDEX ROWID) OF 'UNIONVSOR'6 5 INDEX (RANGE SCAN) OF'UB' (NON-UNIQUE)Statistics----------------------------------------------------------0 recursive calls0 db block gets10 consistent gets0 physical reads0 redo size63735 bytes sent via SQL*Net to client7751 bytes received via SQL*Net from client68 SQL*Net roundtrips to/from client1 sorts (memory)0 sorts (disk)1003 rows processed
36. 用IN来替换OR
SELECT ….FROM LOCATIONWHERE LOC_ID = 10OR LOC_ID = 20OR LOC_ID = 30
高效
SELECT …FROM LOCATIONWHERE LOC_IN IN (10,20,30);
37. 避免在索引列上使用IS NULL和IS NOT NULL
对于单列索引,如果列包含空值,索引中将不存在此记录.
对于复合索引,如果每个列都为空,索引中同样不存在此记录. 如果至少有一个列不为空,则记录存在于索引中.
然而如果所有的索引列都为空,ORACLE将认为整个键值为空,而空不等于空. 因此你可以插入1000条具有相同键值的记录,
SELECT …FROM DEPARTMENTWHERE DEPT_CODE IS NOT NULL;
SELECT …FROM DEPARTMENTWHERE DEPT_CODE >=0;
38. 总是使用索引的第一个列
只有在它的第一个列(leading column)被where子句引用时,优化器才会选择使用该索引.
SQL>create table multiindexusage ( inda number ,indb number ,descr varchar2(10));Tablecreated.SQL>create index multindex on multiindexusage(inda,indb);Indexcreated.SQL>set autotrace traceonlySQL> select * from multiindexusage where inda = 1;ExecutionPlan----------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE1 0 TABLE ACCESS (BY INDEX ROWID)OF 'MULTIINDEXUSAGE'2 1 INDEX (RANGE SCAN) OF'MULTINDEX' (NON-UNIQUE)SQL>select * from multiindexusage where indb= 1;ExecutionPlan----------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE1 0 TABLE ACCESS (FULL) OF'MULTIINDEXUSAGE'
39. ORACLE内部操作
ORACLE Clause 内部操作ORDER BY SORT ORDER BYUNION UNION-ALLMINUS MINUSINTERSECT INTERSECTDISTINCT,MINUS,INTERSECT,UNIONSORT UNIQUEMIN,MAX,COUNT SORT AGGREGATEGROUP BY SORT GROUP BYROWNUM COUNT or COUNT STOPKEYQueries involving Joins SORT JOIN,MERGE JOIN,NESTED LOOPSCONNECT BY CONNECT BY
40. 用UNION-ALL 替换UNION ( 如果有可能的话)
SELECT ACCT_NUM,BALANCE_AMTFROM DEBIT_TRANSACTIONSWHERE TRAN_DATE = '31-DEC-95'UNIONSELECT ACCT_NUM,BALANCE_AMTFROM DEBIT_TRANSACTIONSWHERE TRAN_DATE = '31-DEC-95'
高效:
SELECT ACCT_NUM,BALANCE_AMTFROM DEBIT_TRANSACTIONSWHERE TRAN_DATE = '31-DEC-95'UNION ALLSELECT ACCT_NUM,BALANCE_AMTFROM DEBIT_TRANSACTIONSWHERE TRAN_DATE = '31-DEC-95'
这个操作会使用到SORT_AREA_SIZE这块内存. 对于这块内存的优化也是相当重要的. 下面的SQL可以用来查询排序的消耗量
Select substr(name,1,25) "Sort Area Name",substr(value,1,15)"Value"from v$sysstatwhere name like 'sort%'
41. 使用提示(Hints)
SELECT *+ FULL(EMP) */ * FROM EMP WHERE EMPNO = 7893;
你就可以使用CACHE hint 来告诉优化器把资料保留在SGA中. 通常CACHE hint 和 FULL hint 一起使用.
SELECT *+ FULL(WORKER) CACHE(WORKER)*/ *FROMWORK;
SELECT *+ INDEX(LODGING) */ LODGING FROM LODGING WHERE MANAGER = 'BILL GATES';
在这种情况下,你可以用INDEX hint强制ORACLE使用该索引.
ORACLE hints 还包括ALL_ROWS,FIRST_ROWS,RULE,USE_NL,USE_MERGE,USE_HASH 等等.
这是一个很有技巧性的工作. 建议只针对特定的,少数的SQL进行hint的优化.
42. 用WHERE替代ORDER BY
DEPT_CODE PK NOT NULLDEPT_DESC NOT NULLDEPT_TYPE NULL
SELECTDEPT_CODEFROM DEPTORDER BY DEPT_TYPEEXPLAIN PLAN:SORT ORDER BYTABLE ACCESS FULL
高效: (使用索引)
SELECT DEPT_CODEFROM DEPTWHERE DEPT_TYPE > 0EXPLAIN PLAN:TABLE ACCESS BY ROWID ON EMPINDEX RANGE SCAN ON DEPT_IDX
SQL> select * from emp order by empno;ExecutionPlan----------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE1 0 TABLE ACCESS (BY INDEX ROWID)OF 'EMP'2 1 INDEX (FULL SCAN) OF 'EMPNO'(UNIQUE)
43. 避免改变索引列的类型.
SELECT … FROM EMP WHERE EMPNO = '123'
SELECT … FROM EMP WHERE EMPNO = TO_NUMBER('123')
SELECT … FROM EMP WHERE EMP_TYPE = 123
SELECT … FROM EMP WHERE TO_NUMBER(EMP_TYPE)=123
44. 需要当心的WHERE子句
SELECT ACCOUNT_NAMEFROM TRANSACTIONWHERE AMOUNT !=0;
SELECT ACCOUNT_NAMEFROM TRANSACTIONWHERE AMOUNT >0;
SELECT ACCOUNT_NAME,AMOUNTFROM TRANSACTIONWHERE ACCOUNT_NAME||ACCOUNT_TYPE='AMEXA';
使用索引:
SELECT ACCOUNT_NAME,AMOUNTFROM TRANSACTIONWHERE ACCOUNT_NAME = 'AMEX'AND ACCOUNT_TYPE=' A';
SELECT ACCOUNT_NAME,AMOUNTFROM TRANSACTIONWHERE AMOUNT + 3000 >5000;
使用索引:
SELECT ACCOUNT_NAME,AMOUNTFROM TRANSACTIONWHERE AMOUNT > 2000 ;
SELECT ACCOUNT_NAME,AMOUNTFROM TRANSACTIONWHERE ACCOUNT_NAME = NVL(:ACC_NAME,ACCOUNT_NAME);
使用索引:
SELECT ACCOUNT_NAME,AMOUNTFROM TRANSACTIONWHERE ACCOUNT_NAME LIKE NVL(:ACC_NAME,'%');
CREATE INDEX EMP_I ON EMP(UPPER(ename)); *建立基于函数的索引*/SELECT * FROM emp WHERE UPPER(ename) ='BLACKSNAIL'; *将使用索引*/
45. 连接多个扫描
SELECT *FROM LODGINGWHERE MANAGER IN ('BILL GATES','KEN MULLER');
优化器可能将它转换成以下形式
SELECT *FROM LODGINGWHERE MANAGER = 'BILL GATES' OR MANAGER = 'KEN MULLER';
返回的ROWID用来访问LODGING表的记录(通过TABLE ACCESS BY ROWID 的方式). 最后两组记录以连接(concatenation)的形式被组合成一个单一的集合.
ExplainPlan :SELECT STATEMENT Optimizer=CHOOSECONCATENATIONTABLEACCESS (BY INDEX ROWID) OF LODGINGINDEX(RANGE SCAN ) OF LODGING$MANAGER (NON-UNIQUE)TABLEACCESS (BY INDEX ROWID) OF LODGINGINDEX(RANGE SCAN ) OF LODGING$MANAGER (NON-UNIQUE)
46. CBO下使用更具选择性的索引
47. 避免使用耗费资源的操作
GROUP BY会触发嵌入排序(NESTED SORT) ;
这样,每个查询需要执行一次排序,然后在执行UNION时,又一个唯一排序(SORT UNIQUE)操作被执行而且它只能在前面的嵌入排序结束后才能开始执行. 嵌入的排序的深度会大大影响查询的效率.
如果你的数据库的SORT_AREA_SIZE调配得好,使用UNION,MINUS,INTERSElECT也是可以考虑的,毕竟它们的可读性很强
48. 优化GROUP BY
SELECT JOB ,AVG(SAL)FROM EMPGROUP JOBHAVING JOB = 'PRESIDENT'OR JOB = 'MANAGER'
高效:
SELECT JOB ,AVG(SAL)FROM EMPWHERE JOB = 'PRESIDENT'OR JOB = 'MANAGER'GROUP JOB
49. 使用显式的光标(CURSORs)
第一次检索记录,
第二次检查TOO MANY ROWS 这个exception .
而显式光标不执行第二次操作.
50. 优化EXPORT和IMPORT
可以提高EXPORT和IMPORT的速度.
51. 分离表和索引
文章转载自数据与人,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




