1、不会使用索引的情况
在以下情况下,Oracle不会使用索引:
(1)、 Where 中只要有一个条件是 IS NULL 或IS NOT NULL操作
判断字段是否为空一般是不会应用索引的,因为B树索引是不索引空值的。
如: a is not null 不会用到索引
修正:a、用其它相同功能的操作运算代替,如改为 a>0 等。
b、不允许字段为空,而用一个缺省值代替空值,如业扩申请中状态字段不允许为空,缺省为申请。
不允许字段为空,而用一个缺省值代替空值,如业扩申请中状态字段不允许为空,缺省为申请。
(2).联接列 ‘||’
不使用索引: select * from employss where first_name||''||last_name ='Beill Cliton';
修正:Select * from employee where first_name ='Beill' and last_name ='Cliton';
(3)带通配符(%)的like语句
不使用索引:select * from employee where last_name like '%cliton%';
使用索引:select * from employee where last_name like 'c%';
(4) NOT
not 不会使用索引
(5) <> , != 操作符(不等于)
索引只能告诉你什么存在于表中, 而不能告诉
你什么不存在于表中.
不等于操作符是永远不会用到索引的,因此对它的处理只会产生全表扫描。
推荐方案:用其它相同功能的操作运算代替,如 a<>0 改为 a>0 or a<0
(6)、在索引列上使用计算
低效: SELECT … FROM DEPT WHERE SAL * 12 > 25000;
高效: SELECT … FROM DEPT WHERE SAL > 25000/12;
2、Order by语句 的字段加上索引
仔细检查order by语句以找出非索引项或者表达式,它们会降低性能。解决这个问题的办法就是重写order by语句以使用索引,也可以为所使用的列建立另外一个索引,同时应绝对避免在order by子句中使用表达式。
3、 EXISTS 优于 IN,NOT EXISTS 优于NOT IN
EXISTS比使用IN通常查询速度快。
执行EXISTS过程:运用EXISTS子句不管子查询从表中抽取什么数据它只查看where子句,这样优化器就不必遍历整个表而仅根据索引就可完成工作(这里假定在where语句中使用的列存在索引),通过使用EXISTS,Oracle系统会首先检查主查询,然后运行子查询直到它找到第一个匹配项,这就节省了时间。
执行IN过程:首先执行子查询,并将获得的结果列表存放在在一个加了索引的临时表中。在执行子查询之前,系统先将主查询挂起,待子查询执行完毕,存放在临时表中以后再执行主查询。
低效: SELECT * FROM EMP WHERE EMPNO > 0 AND DEPTNO IN (SELECT DEPTNO FROM DEPT WHERE LOC = ‘MELB')
高效: SELECT * FROM EMP WHERE EMPNO > 0 AND EXISTS (SELECT ‘X' FROM DEPT WHERE
DEPT.DEPTNO = EMP.DEPTNO AND LOC = ‘MELB')
低效: SELECT … FROM EMP WHERE DEPT_NO NOT IN (SELECT DEPT_NO FROM DEPT WHERE DEPT_CAT='A');
高效:SELECT …. FROM EMP E WHERE NOT EXISTS (SELECT ‘X' FROM DEPT D WHERE D.DEPT_NO = E.DEPT_NO AND DEPT_CAT = ‘A');
4、union all操作符替代union
union在进行表链接后会筛选掉重复的记录,所以在表链接后会对所产生的结果集进行排序运算,删除重复的记录再返回结果。
union all操作只是简单的将两个结果合并后就返回。
实际大部分应用中是不会产生重复的记录
5、SELECT子句中避免使用 ‘ * ‘ ,而是查出需要的列
ORACLE在解析的过程中, 会将’*’ 依次转换成所有的列名, 这个工作是通过查询数据字典完成的, 这意味着将耗费更多的时间.
6、用DECODE函数来减少处理时间
使用DECODE函数可以避免重复扫描相同记录或重复连接相同的表.
例如这两条查询:
SELECT COUNT(*),SUM(SAL) FROM EMP WHERE DEPT_NO = 0020 AND ENAME LIKE ‘SMITH%’;
SELECT COUNT(*),SUM(SAL) FROM EMP WHERE DEPT_NO = 0030 AND 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_SAL FROM EMP WHERE ENAME LIKE ‘SMITH%’;
7、用Where子句替换HAVING子句
避免使用HAVING子句, HAVING 只会在检索出所有记录之后才对结果集进行过滤. 这个处理需要排序,总计等操作. 如果能通过WHERE子句限制记录的数目,那就能减少这方面的开销.
例如:
低效:SELECT REGION,AVG(LOG_SIZE) FROM LOCATION GROUP BY REGION HAVING REGION REGION != ‘SYDNEY’ AND REGION != ‘PERTH’
高效:SELECT REGION,AVG(LOG_SIZE) FROM LOCATION WHERE REGION REGION != ‘SYDNEY’ AND REGION != ‘PERTH’ GROUP BY REGION
HAVING 中的条件一般用于对一些集合函数的比较,如COUNT() 等等. 除此而外,一般的条件应该写在WHERE子句中
8、使用表的别名(Alias)
当在SQL语句中连接多个表时, 请使用表的别名并把别名前缀于每个Column上.这样一来,就可以减少解析的时间并减少那些由Column歧义引起的语法错误.
9、用表连接替换EXISTS
通常来说 , 采用表连接的方式比EXISTS更有效率
低效:SELECT ENAME FROM EMP E WHERE EXISTS (SELECT ‘X' FROM DEPT WHERE DEPT_NO = E.DEPT_NO AND DEPT_CAT = ‘A');
高效:SELECT ENAME FROM DEPT D,EMP E WHERE E.DEPT_NO = D.DEPT_NO AND DEPT_CAT = ‘A'
前者的执行路径包括FILTER,后者使用NESTED LOOP
10、用EXISTS替换DISTINCT
当提交一个包含一对多表信息(比如部门表和雇员表)的查询时,避免在SELECT子句中使用DISTINCT. 一般可以考虑用EXIST替换
低效: SELECT DISTINCT DEPT_NO,DEPT_NAME FROM DEPT D,EMP E WHERE D.DEPT_NO = E.DEPT_NO
高效: SELECT DEPT_NO,DEPT_NAME FROM DEPT D WHERE EXISTS ( SELECT ‘X' FROM EMP E WHERE E.DEPT_NO = D.DEPT_NO);
11、用IN来替换OR
低效 SELECT…. FROM LOCATION WHERE LOC_ID = 10 OR LOC_ID = 20 OR LOC_ID = 30
高效 SELECT… FROM LOCATION WHERE LOC_IN IN (10,20,30);
12、总是使用索引的第一个列
如果索引是建立在多个列上, 只有在它的第一个列(leading column)被where子句引用时,优化器才会选择使用该索引.
13、共享SQL语句
为了不重复解析相同的SQL语句, 在第一次解析之后, ORACLE将SQL语句存放在内存中.这块位于系统全局区域SGA(system global area)的共享池(shared buffer pool)中的内存可以被所有的数据库用户共享. 因此,当你执行一个SQL语句(有时被称为一个游标)时,如果它和之前的执行过的语句完全相同, ORACLE就能很快获得已经被解析的语句以及最好的执行路径. ORACLE的这个功能大大地提高了SQL的执行性能并节省了内存的使用.
当你向ORACLE 提交一个SQL语句,ORACLE会首先在这块内存中查找相同的语句.这里需要注明的是,ORACLE对两者采取的是一种严格匹配,要达成共享,SQL语句必须完全相同(包括空格,换行等).共享的语句必须满足三个条件:
A、字符级的比较: 当前被执行的语句和共享池中的语句必须完全相同.(包括大小写)
B、两个语句所指的对象必须完全相同:
C、两个SQL语句中必须使用相同的名字的绑定变量(bind variables)
14、两表连接,大表的在前,小表的在后作为基础表
ORACLE的解析器按照从右到左的顺序处理FROM子句中的表名, 因此FROM子句中写在最后的表(基础表 driving table)将被最先处理. 在FROM子句中包含多个表的情况下,你必须选择记录条数最少的表作为基础表.当ORACLE处理多个表时, 会运用排序及合并的方式连接它们.首先,扫描第一个表(FROM子句中最后的那个表) 并对记录进行派序,然后扫描第二个表(FROM子句中最后第二个表),最后将所有从第二个表中检索出的记录与第一个表中合适记录进行合并.
高效:select count(*) from big_Tab, small_Tab
高效:select count(*) from small_Tab, big_Tab
15、3个以上的表连接查询, 选择交叉表在最后作为基础表, 交叉表是指那个被其他表所引用的表
如果EMP表描述了LOCATION表和CATEGORY表的交集.,则EMP放在最后
低效:SELECT * FROM EMP E , LOCATION L , CATEGORY C WHERE E.CAT_NO = C.CAT_NO AND E.LOCN = L.LOCN AND E.EMP_NO BETWEEN 1000 AND 2000
高效:SELECT * FROM LOCATION L , CATEGORY C, EMP E WHERE E.EMP_NO BETWEEN 1000 AND 2000 AND E.CAT_NO = C.CAT_NO AND E.LOCN = L.LOCN
16、 WHERE子句中的连接顺序 (可以过滤掉最大数量记录的条件必须写在WHERE子句的末尾)
ORACLE采用自下而上的顺序解析WHERE子句,根据这个原理,表之间的连接必须写在其他WHERE条件之前, 那些可以过滤掉最大数量记录的条件必须写在WHERE子句的末尾.
低效:SELECT … FROM EMP E WHERE SAL > 50000 AND JOB = ‘MANAGER’
AND 25 < (SELECT COUNT(*) FROM EMP WHERE MGR=E.EMPNO);
高效:SELECT … FROM EMP E WHERE 25 < (SELECT COUNT(*) FROM EMP WHERE MGR=E.EMPNO) AND SAL > 50000 AND
JOB = ‘MANAGER’;
17、减少访问数据库的次数
低效:两条
SELECT EMP_NAME , SALARY , GRADE FROM EMP WHERE EMP_NO = 342;
SELECT EMP_NAME , SALARY , GRADE FROM EMP WHERE EMP_NO = 291
高效:合并为一条
SELECT A.EMP_NAME , A.SALARY , A.GRADE, B.EMP_NAME , B.SALARY , B.GRADE FROM EMP A, EMP B WHERE A.EMP_NO = 342 OR B.EMP_NO = 291;
18、使用DECODE函数来减少处理时间
使用DECODE函数可以避免重复扫描相同记录或重复连接相同的表.
SELECT COUNT(*),SUM(SAL) FROM EMP WHERE DEPT_NO = 0020 AND ENAME LIKE ‘SMITH%’;
SELECT COUNT(*),SUM(SAL) FROM EMP WHERE DEPT_NO = 0030 AND 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_SAL FROM EMP WHERE ENAME LIKE ‘SMITH%’;
19 最高效的删除重复记录方法 ( 因为使用了ROWID)
DELETE FROM EMP E WHERE E.ROWID > (SELECT MIN(X.ROWID) FROM EMP X WHERE X.EMP_NO = E.EMP_NO );
20、用TRUNCATE替代DELETE
当删除表中的记录时,在通常情况下, 回滚段(rollback segments ) 用来存放可以被恢复的信息. 如果你没有COMMIT事务,ORACLE会将数据恢复到删除之前的状态(准确地说是恢复到执行删除命令之前的状况).
而当运用TRUNCATE时, 回滚段不再存放任何可被恢复的信息.当命令运行后,数据不能被恢复.因此很少的资源被调用,执行时间也会很短.
(译者按: TRUNCATE只在删除全表适用,TRUNCATE是DDL不是DML)
21、 尽量多使用COMMIT
只要有可能,在程序中尽量多使用COMMIT, 这样程序的性能得到提高,需求也会因为COMMIT所释放的资源而减少:
COMMIT所释放的资源:
a.回滚段上用于恢复数据的信息.
b.被程序语句获得的锁
c.redo log buffer 中的空间
d.ORACLE为管理上述3种资源中的内部花费
22、用Where子句替换HAVING子句
HAVING 中的条件一般用于对一些集合函数的比较,如COUNT() 等等. 除此而外,一般的条件应该写在WHERE子句中
HAVING 只会在检索出所有记录之后才对结果集进行过滤. 这个处理需要排序,总计等操作.
低效: SELECT REGION,AVG(LOG_SIZE) FROM LOCATION GROUP BY REGION HAVING REGION REGION != ‘SYDNEY’ AND REGION != ‘PERTH’
高效: SELECT REGION,AVG(LOG_SIZE) FROM LOCATION WHERE REGION REGION != ‘SYDNEY’ AND REGION != ‘PERTH’ GROUP BY REGION
23、子查询的SQL语句中,要特别注意减少对表的查询
低效: SELECT TAB_NAME FROM TABLES WHERE TAB_NAME = ( SELECT TAB_NAME FROM TAB_COLUMNS WHERE VERSION = 604) AND DB_VER= ( SELECT DB_VER FROM TAB_COLUMNS WHERE VERSION = 604)
高效: SELECT TAB_NAME FROM TABLES WHERE (TAB_NAME, DB_VER) = ( SELECT TAB_NAME, DB_VER FROM TAB_COLUMNS WHERE VERSION = 604)
低效: UPDATE EMP SET EMP_CAT = (SELECT MAX(CATEGORY) FROM EMP_CATEGORIES), SAL_RANGE = (SELECT MAX(SAL_RANGE) FROM EMP_CATEGORIES) WHERE EMP_DEPT = 0020;
高效: UPDATE EMP SET (EMP_CAT, SAL_RANGE) = (SELECT MAX(CATEGORY) , MAX(SAL_RANGE) FROM EMP_CATEGORIES) WHERE EMP_DEPT = 0020;
24、识别’低效执行’的SQL语句
用下列SQL工具找出低效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_TEXT FROM V$SQLAREA WHERE EXECUTIONS>0 AND BUFFER_GETS > 0 AND (BUFFER_GETS-DISK_READS)/BUFFER_GETS < 0.8 ORDER BY 4 DESC;
25、定期的重构索引
ALTER INDEX <INDEXNAME> REBUILD <TABLESPACENAME>
26、强制索引失效
如果两个或以上索引具有相同的等级,你可以强制命令ORACLE优化器使用其中的一个(通过它,检索出的记录数量少) .
举例:
SELECT ENAME FROM EMP
WHERE EMPNO = 7935
AND DEPTNO + 0 = 10 /*DEPTNO上的索引将失效*/
AND EMP_TYPE || ‘’ = ‘A’ /*EMP_TYPE上的索引将失效*/
27、用>=替代>
低效: SELECT * FROM EMP WHERE DEPTNO >3
高效: SELECT * FROM EMP WHERE DEPTNO >=4
28、避免改变索引列的类型.
假设EMP_TYPE 是一个字符类型的索引列.
SELECT … FROM EMP WHERE EMP_TYPE = 123
这个语句被ORACLE 转换为:
SELECT … FROM EMP WHERE TO_NUMBER(EMP_TYPE)=123
此时将不会使用索引。
正确的应该写为 SELECT … FROM EMP WHERE EMP_TYPE = ’123‘
29、避免使用耗费资源的操作
带有DISTINCT,UNION,MINUS,INTERSECT,ORDER BY 的SQL 语句会启动SQL 引擎
执行耗费资源的排序(SORT)功能. DISTINCT 需要一次排序操作, 而其他的至少需要执行两次
排序.
例如,一个UNION 查询,其中每个查询都带有GROUP BY 子句, GROUP BY 会触发嵌入排序
(NESTED SORT) ; 这样, 每个查询需要执行一次排序, 然后在执行UNION 时, 又一个唯一排序
(SORT UNIQUE)操作被执行而且它只能在前面的嵌入排序结束后才能开始执行. 嵌入的排序的深
度会大大影响查询的效率.
通常, 带有UNION, MINUS , INTERSECT 的SQL 语句都可以用其他方式重写.
30、分离表和索引
将你的表和索引建立在不同的表空间内。
不要将不属于ORACLE 内部系统的对象存放到SYSTEM表空间里。
确保数据表空间和索引表空间置于不同的硬盘上。