优化心得
我们经常所说的优化
,
主要就是通过建立相关的索引
,
来提供
SQL
语句执行的速度
.
因此
,
如何选择索引
,
如何利用索引提供执行的速度就成为优化的主要任务
.
索引无效的情况
:
如果发现
Oracle
在有索引的情况下,没有使用索引,这并不是
Oracle
的优化器出错。在有些情况下,
Oracle
确实会选择全表扫描(
FullTableScan
)
,
而非索引扫描(
IndexScan
)。表未做
statistics,
或者
statistics
陈旧,导致
Oracle
判断失误。
关于索引使用的一些误区和体会
1.
索引不是越多越好。特别是大量从来或者几乎不用的索引,对系统只有损害。
OLTP
系统每表超过
5
个索引即会降低性能,而且在一个
sql
中,
Oracle
从不能使用超过
5
个索引。
2.
很多时候,单列索引不如复合索引有效率。
3.
用于多表连结的字段,加上索引会很有作用。
4.
Oracle
要使用一个索引,有一些最基本的条件:
A.
where
子句中的这个字段,必须是复合索引的第一个字段
(
引导字段
);
B.
where
子句中的这个字段,不应该参与任何形式的计算
;
C.
where
子句中的这个字段,如果参与了函数运算
,
则应该建立相关的函数索引
;
避免在索引列上使用计算.
WHERE
子句中,如果索引列是函数的一部分.优化器将不使用索引而使用全表扫描.
避免在索引列上使用
NOT
通常, 我们要避免在索引列上使用
NOT, NOT
会产生在和在索引列上使用函数相同的影响
.
当
ORACLE”
遇到
”NOT,
他就会停止使用索引转而执行全表扫描
.
用
>=
替代
>
如果
DEPTNO
上有一个索引
,
高效
:
SELECT * FROM EMP WHERE DEPTNO >=4
低效
:
SELECT * FROM EMP WHERE DEPTNO >3
两者的区别在于
,
前者
DBMS
将直接跳到第一个
DEPT
等于
4
的记录而后者将首先定位到
DEPTNO=3
的记录并且向前扫描到第一个
DEPT
大于
3
的记录
.
用
UNION
替换
OR (
适用于索引列
)
通常情况下
,
用
UNION
替换
WHERE
子句中的
OR
将会起到较好的效果
.
对索引列使用
OR
将造成全表扫描
.
注意
,
以上规则只针对多个索引列有效
.
如果有
column
没有被索引
,
查询效率可能会因为你没有选择
OR
而降低
.
用
IN
来替换
OR
避免在索引列上使用
IS NULL
和
IS NOT NULL
避免在索引中使用任何可以为空的列,
ORACLE
将无法使用该索引
.对于单列索引,如果列包含空值,索引中将不存在此记录
.
对于复合索引,如果每个列都为空,索引中同样不存在此记录
.
如果至少有一个列不为空,则记录存在于索引中.
总是使用索引的第一个列
如果索引是建立在多个列上
,
只有在它的第一个列
(leading column)
被
where
子句引用时
,
优化器才会选择使用该索引
.
SQL> create table multiindexusage ( inda number , indb number , descr varchar2(10));
Table created.
SQL> create index multindex on multiindexusage(inda,indb);
Index created.
SQL> set autotrace traceonly
SQL> select * from multiindexusage where inda = 1;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'MULTIINDEXUSAGE'
2 1 INDEX (RANGE SCAN) OF 'MULTINDEX' (NON-UNIQUE)
SQL> select * from multiindexusage where indb = 1;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (FULL) OF 'MULTIINDEXUSAGE'
很明显
,
当仅引用索引的第二个列时
,
优化器使用了全表扫描而忽略了索引
用
UNION-ALL
替换
UNION
(
如果有可能的话
)
当
SQL
语句需要
UNION
两个查询结果集合时
,
这两个结果集合会以
UNION-ALL
的方式被合并
,
然后在输出最终结果前进行排序
.
如果用
UNION ALL
替代
UNION,
这样排序就不是必要了
.
效率就会因此得到提高
.
用
WHERE
替代
ORDER BY
ORDER BY
子句只在两种严格的条件下使用索引
.
ORDER BY
中所有的列必须包含在相同的索引中并保持在索引中的排列顺序
.
ORDER BY
中所有的列必须定义为非空
.
需要当心的
WHERE
子句
某些
SELECT
语句中的
WHERE
子句不使用索引
.
这里有一些例子
.
在下面的例子里
, ‘!='
将不使用索引
.
记住
,
索引只能告诉你什么存在于表中
,
而不能告诉你什么不存在于表中
.
CBO
下使用更具选择性的索引
可选择性
(selectivity)
:
比较一下列中唯一键的数量和表中的行数,就可以判断该列的可选择性。如果该列的
”
唯一键的数量
/
表中的行数
”
的比值越接近
1
,则该列的可选择性越高,该列就越适合创建索引,同样索引的可选择性也越高。在可选择性高的列上进行查询时,返回的数据就较少,比较适合使用索引查询。
基于成本的优化器
(CBO, Cost-Based Optimizer)
对索引的选择性进行判断来决定索引的使用是否能提高效率
.
如果索引有很高的选择性
,
那就是说对于每个不重复的索引键值
,
只对应数量很少的记录
.
比如
,
表中共有
100
条记录而其中有
80
个不重复的索引键值
.
这个索引的选择性就是
80/100 = 0.8 .
选择性越高
,
通过索引键值检索出的记录就越少
.
如果索引的选择性很低
,
检索数据就需要大量的索引范围查询操作和
ROWID
访问表的操作
.
也许会比全表扫描的效率更低
.
a.
如果检索数据量超过
30%
的表中记录数
.
使用索引将没有显著的效率提高
.
b.
在特定情况下
,
使用索引也许会比全表扫描慢
,
但这是同一个数量级上的区别
.
而通常情况下
,
使用索引比全表扫描要块几倍乃至几千倍
!
避免使用耗费资源的操作
带有
DISTINCT,UNION,MINUS,INTERSECT,ORDER BY
的
SQL
语句会启动
SQL
引擎
执行耗费资源的排序
(SORT)
功能
. DISTINCT
需要一次排序操作
,
而其他的至少需要执行两次排序
.
例如
,
一个
UNION
查询
,
其中每个查询都带有
GROUP BY
子句
, GROUP BY
会触发嵌入排序
(NESTED SORT) ;
这样
,
每个查询需要执行一次排序
,
然后在执行
UNION
时
,
又一个唯一排序
(SORT UNIQUE)
操作被执行而且它只能在前面的嵌入排序结束后才能开始执行
.
嵌入的排序的深度会大大影响查询的效率
.
通常
,
带有
UNION, MINUS , INTERSECT
的
SQL
语句都可以用其他方式重写
.
优化
GROUP BY
提高
GROUP BY
语句的效率
,
可以通过将不需要的记录在
GROUP BY
之前过滤掉
.
(用
WHERE
来替代
HAVING
)
一、 索引(INDEX)使用的问题
1
. 索引(INDEX),用还是不用?这是个的问题。
是全表扫描还是索引范围扫描主要考虑SQL的查询速度问题。这里主要关心读取的记录的数目。根据DONALD K .BURLESON的说法,使用索引范围扫描的原则是:
对于数据有原始排序的表,读取少于表记录数40%的查询应该使用索引范围扫描。对读取多于表记录数40%的查询应全表扫描。
对于未排序的表,读取少于表记录数7%的查询应该使用索引范围扫描,反之,对读取多于表记录数7%的查询应全表扫描。
注:在不同的书中,对是否使用索引的读取记录的百分比值不太一致,基本上是一个经验值,但是读取记录的百分比越低,使用索引越有效。
2
. 如果列上有建索引,什么SQL查询是有用索引(INDEX)的?什么SQL查询是没有用索引(INDEX)的?
存在下面情况的SQL,不会用到索引:
存在数据类型隐形转换的,如:
select * from staff_member where staff_id=’123’
;
列上有数学运算的,如:
select * from staff_member where salary*2<10000;
使用不等于(<> )运算的,如: select * from staff_member where dept_no<>2001;
使用substr字符串函数的,如: select * from staff_member where substr(last_name,1,4)=’FRED’;
‘%’
通配符在第一个字符的,如:
select * from staff_member where first_name like ‘%DON’;
字符串连接(||)的,如: select * from staff_member where first_name||’’=’DONALD’
3
. 函数的索引
日期类型也是很容易用到的,而且在SQL语句中会使用to_char函数以查询具体的的范围日期。如:select * from staff_member where TO_CHAR(birth_day,’YYYY’)=’2003’; 我们可以建立基于函数的索引如:CREATE INDEX Ind_emp_birth ON staff_member (to_char((birth_day,’YYYY’));
索引使用注意事项
:
1)
、select,update,delete 语句中的子查询应当有规律地查找少于20%的表行.如果一个语句查找的行数超过总行数的20%,它将不能通过使用索引获得性能上的提高.
2
)、索引可能产生碎片,因为记录从表中删除时,相应也从表的索引中删除.表释放的空间可以再用,而索引释放的空间却不能再用.频繁进行删除操作的被索引的表,应当阶段性地重建索引,以避免在索引中造成空间碎片,影响性能.在许可的条件下,也可以阶段性地truncate表,truncate命令删除表中所有记录,也删除索引碎片.
3
)、在使用索引时一定要按索引对应字段的顺序进行引用。
阻止
Oracle
使用索引。以下是一些基本规则:
1,
如果
f1
和
f2
是同一个表的两个字段,则
f1>f2,f1>=f2
2,
f1isnull,f1isnotnull,f1notin,f1!=,f1like‘%pattern%’;
3,
Notexist
5.
WHERE
子句中的连接顺序
ORACLE
采用自下而上的顺序解析
WHERE
子句
,
根据这个原理
,
表之间的连接必须写在其他
WHERE
条件之前
,
那些可以过滤掉最大数量记录的条件必须写在
WHERE
子句的末尾
.
6.
SELECT
子句中避免使用
‘*‘
- 使用DECODE函数来减少处理时间
使用
DECODE
函数可以避免重复扫描相同记录或重复连接相同的表。
8.
整合简单
,
无关联的数据库访问
如果你有几个简单的数据库查询语句
,
你可以把它们整合到一个查询中
(
即使它们之间没有关系
)
例如
:
SELECTNAMEFROMEMP WHEREEMP_NO=1234;
SELECTNAMEFROMDPT WHEREDPT_NO=10;
SELECTNAMEFROMCAT WHERECAT_TYPE=‘RD';
上面的
3
个查询可以被合并成一个
:
SELECTE.NAME,D.NAME,C.NAMEFROMCATC,DPTD,EMPE,DUALX
WHERENVL(‘X',X.DUMMY)=NVL(‘X',E.ROWID(+))
ANDNVL(‘X',X.DUMMY)=NVL(‘X',D.ROWID(+))
ANDNVL(‘X',X.DUMMY)=NVL(‘X',C.ROWID(+))
ANDE.EMP_NO(+)=1234ANDD.DEPT_NO(+)=10ANDC.CAT_TYPE(+)=‘RD';
9.
删除重复记录
最高效的删除重复记录方法
(
因为使用了
ROWID)
DELETEFROMEMPE WHEREE.ROWID>(SELECTMIN(X.ROWID) FROMEMPX
WHEREX.EMP_NO=E.EMP_NO);
10.
用
TRUNCATE
替代
DELETE
当删除表中的记录时
,
在通常情况下
,
回滚段
(rollbacksegments)
用来存放可以被恢复的信息
.
如果你没有
COMMIT
事务
,ORACLE
会将数据恢复到删除之前的状态
(
准确地说是恢复到执行删除命令之前的状况
)
,而当运用
TRUNCATE
时
,
回滚段不再存放任何可被恢复的信息
.
当命令运行后
,
数据不能被恢复
.
因此很少的资源被调用
,
执行时间也会很短。
(
注:
TRUNCATE
只在删除全表适用
,TRUNCATE
是
DDL
不是
DML)
11.
尽量多使用
COMMIT
只要有可能
,
在程序中尽量多使用
COMMIT,
这样程序的性能得到提高
,
需求也会因为
COMMIT
所释放的资源而减少
,COMMIT
所释放的资源
:
a.
回滚段上用于恢复数据的信息。
b.
被程序语句获得的锁。
c.redologbuffer
中的空间。
d.Oracle
为管理上述
3
种资源中的内部花费。
SQL
语句优化的心得
用
Where
子句替换
HAVING
子句
避免使用
HAVING
子句
, HAVING
只会在检索出所有记录之后才对结果集进行过滤
.
这个处理需要排序
,
总计等操作
.
如果能通过
WHERE
子句限制记录的数目
,
那就能减少这方面的开销
.
减少对表的查询
在含有子查询的
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;
通过内部函数提高
SQL
效率
.
使用表的别名
(Alias)
当在
SQL
语句中连接多个表时
,
请使用表的别名并把别名前缀于每个
Column
上
.
这样一来
,
就可以减少解析的时间并减少那些由
Column
歧义引起的语法错误
.
用
EXISTS
替代
IN
在许多基于基础表的查询中
,
为了满足一个条件
,
往往需要对另一个表进行联接
.
在这种情况下
,
使用
EXISTS(
或
NOT EXISTS)
通常将提高查询的效率
.
低效
:
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'
)
用
NOT EXISTS
替代
NOT IN
在子查询中
,NOT IN
子句将执行一个内部的排序和合并
.
无论在哪种情况下
,NOT IN
都是最低效的
(
因为它对子查询中的表执行了一个全表遍历
).
为了避免使用
NOT IN ,
我们可以把它改写成外连接
(Outer Joins)
或
NOT EXISTS.
例如
:
SELECT … FROM EMP WHERE DEPT_NO NOT IN
(
SELECT DEPT_NO FROM DEPT WHERE DEPT_CAT='A'
);
为了提高效率
.
改写为
:
(
方法一
:
高效
)
SELECT …. FROM EMP A,DEPT B WHERE A.DEPT_NO = B.DEPT(+)
AND B.DEPT_NO IS NULL AND B.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'
);
用表连接替换
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' ;
用
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
);
EXISTS
使查询更为迅速
,
因为
RDBMS
核心模块将在子查询的条件一旦满足后
,
立刻返回结果
.
用
EXPLAIN PLAN
分析
SQL
语句
EXPLAIN PLAN
是一个很好的分析
SQL
语句的工具
,
它甚至可以在不执行
SQL
的情况下分析语句
.
通过分析
,
我们就可以知道
ORACLE
是怎么样连接表
,
使用什么方式扫描表
(
索引扫描或全表扫描
)
以及使用到的索引名称
.
你需要按照从里到外
,
从上到下的次序解读分析的结果
. EXPLAIN PLAN
分析的结果是用缩进的格式排列的
,
最内部的操作将被最先解读
,
如果两个操作处于同一层中
,
带有最小操作号的将被首先执行
.
NESTED LOOP
是少数不按照上述规则处理的操作
,
正确的执行路径是检查对
NESTED LOOP
提供数据的操作
,
其中操作号最小的将被最先处理
.
举例
:
SQL> list
1 SELECT *
2 FROM dept, emp
3* WHERE emp.deptno = dept.deptno
SQL> set autotrace traceonly /*traceonly
可以不显示执行结果
*/
SQL> /
14 rows selected.
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 NESTED LOOPS
2 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 calls
2 db block gets
30 consistent gets
0 physical reads
0 redo size
2598 bytes sent via SQL*Net to client
503 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
14 rows processed
通过以上分析
,
可以得出实际的执行步骤是
:
1. TABLE ACCESS (FULL) OF 'EMP'
2. INDEX (UNIQUE SCAN) OF 'PK_DEPT' (UNIQUE)
3. TABLE ACCESS (BY INDEX ROWID) OF 'DEPT'
4. NESTED LOOPS (JOINING 1 AND 3)
SQL
语句编写注意问题
下面就某些SQL语句的where子句编写中需要注意的问题作详细介绍。在这些where子句中,即使某些列存在索引,但是由于编写了劣质的SQL,系统在运行该SQL语句时也不能使用该索引,而同样使用全表扫描,这就造成了响应速度的极大降低。
1. IS NULL
与 IS NOT NULL
不能用null作索引,任何包含null值的列都将不会被包含在索引中。即使索引有多列这样的情况下,只要这些列中有一列含有null,该列就会从索引中排除。也就是说如果某列存在空值,即使对该列建索引也不会提高性能。
任何在where子句中使用is null或is not null的语句优化器是不允许使用索引的。
2.
联接列
对于有联接的列,即使最后的联接值为一个静态值,优化器是不会使用索引的。下面是一个采用联接查询的SQL语句,
select * from employss where first_name||''||last_name ='Beill Cliton';
如何改写?
3.
带通配符(%)的like语句
同样以上面的例子来看这种情况。目前的需求是这样的,要求在职工表中查询名字中包含cliton的人。可以采用如下的查询SQL语句:
select * from employee where last_name like '%cliton%';
这里由于通配符(%)在搜寻词首出现,所以Oracle系统不使用last_name的索引。在很多情况下可能无法避免这种情况,但是一定要心中有底,通配符如此使用会降低查询速度。然而当通配符出现在字符串其他位置时,优化器就能利用索引。在下面的查询中索引得到了使用:
select * from employee where last_name like 'c%';
4. Order by
语句
ORDER BY
语句决定了Oracle如何将返回的查询结果排序。Order by语句对要排序的列没有什么特别的限制,也可以将函数加入列中(象联接或者附加等)。任何在Order by语句的非索引项或者有计算表达式都将降低查询速度。
仔细检查order by语句以找出非索引项或者表达式,它们会降低性能。解决这个问题的办法就是重写order by语句以使用索引,也可以为所使用的列建立另外一个索引,同时应绝对避免在order by子句中使用表达式。
5. NOT
我们在查询时经常在where子句使用一些逻辑表达式,如大于、小于、等于以及不等于等等,也可以使用and(与)、or(或)以及not(非)。NOT可用来对任何逻辑运算符号取反。下面是一个NOT子句的例子:
... where not (status ='VALID')
如果要使用NOT,则应在取反的短语前面加上括号,并在短语前面加上NOT运算符。NOT运算符包含在另外一个逻辑运算符中,这就是不等于(<>)运算符。换句话说,即使不在查询where子句中显式地加入NOT词,NOT仍在运算符中,见下例:
... where status <>'INVALID';
再看下面这个例子:
select * from employee where salary<>3000;
对这个查询,可以改写为不使用NOT:
select * from employee where salary<3000 or salary>3000;
以上改写还能更优化,用UNION来完成
虽然这两种查询的结果一样,但是第二种查询方案会比第一种查询方案更快些。第二种查询允许Oracle对salary列使用索引,而第一种查询则不能使用索引。
6. IN
和EXISTS
有时候会将一列和一系列值相比较。最简单的办法就是在where子句中使用子查询。在where子句中可以使用两种格式的子查询。
第一种格式是使用IN操作符:
... where column in(select * from ... where ...);
第二种格式是使用EXIST操作符:
... where exists (select 'X' from ...where ...);
我相信绝大多数人会使用第一种格式,因为它比较容易编写,而实际上第二种格式要远比第一种格式的效率高。在Oracle中可以几乎将所有的IN操作符子查询改写为使用EXISTS的子查询。
第二种格式中,子查询以‘select 'X'开始。运用EXISTS子句不管子查询从表中抽取什么数据它只查看where子句。这样优化器就不必遍历整个表而仅根据索引就可完成工作(这里假定在where语句中使用的列存在索引)。相对于IN子句来说,EXISTS使用相连子查询,构造起来要比IN子查询困难一些。
通过使用EXIST,Oracle系统会首先检查主查询,然后运行子查询直到它找到第一个匹配项,这就节省了时间。Oracle系统在执行IN子查询时,首先执行子查询,并将获得的结果列表存放在在一个加了索引的临时表中。在执行子查询之前,系统先将主查询挂起,待子查询执行完毕,存放在临时表中以后再执行主查询。这也就是使用EXISTS比使用IN通常查询速度快的原因。
同时应尽可能使用NOT EXISTS来代替NOT IN,尽管二者都使用了NOT(不能使用索引而降低速度),NOT EXISTS要比NOT IN查询效率更高。
SQL
语句排序优化
1.
排序发生的情况:
SQL
中包含group by 子句
SQL
中包含order by 子句
SQL
中包含 distinct 子句
SQL
中包含 minus 或 union操作
创建索引时
SQL
子查询的调整
1、
理解关联子查询和非关联子查询。
下面是一个非关联子查询:
select staff_name from staff_member where staff_id
in (select staff_id from staff_func);
而下面是一个关联子查询:
select staff_name from staff_member where staff_id in
(
select staff_id from staff_func where staff_member.staff_id=staff_func.staff_id
);
以上返回的结果集是相同的,可是它们的执行开销是不同的:
非关联查询的开销——非关联查询时子查询只会执行一次,而且结果是排序好的,并保存在一个ORACLE的临时段中,其中的每一个记录在返回时都会被父查询所引用。在子查询返回大量的记录的情况下,将这些结果集排序,以及将临时数据段进行排序会增加大量的系统开销。
关联查询的开销——对返回到父查询的的记录来说,子查询会每行执行一次。因此,我们必须保证任何可能的时候子查询用到索引。
2
、EXISTS子句和IN子句
带IN的关联子查询是多余的,因为IN子句和子查询中相关的操作的功能是一样的。如: select staff_name from staff_member where staff_id in (select staff_id from staff_func where staff_member.staff_id=staff_func.staff_id);
为非关联子查询指定EXISTS子句是不适当的,因为这样会产生笛卡乘积。如:
select staff_name from staff_member where staff_id Exists (select staff_id from staff_func);
尽量不要使用NOT IN子句。使用MINUS 子句都比NOT IN 子句快,虽然使用MINUS子句要进行两次查询:
select staff_name from staff_member where staff_id in (select staff_id from staff_member MINUS select staff_id from staff_func where func_id like ‘81%’);
3
、 任何可能的时候,用标准连接或内嵌视图改写子查询。
基于成本优化器(CBO):
(1)ORACLE 8i 以上版本更多地使用成本优化器,因为它更加智能;
(2)通过optimizer_mode=all_rows 或 first_rows来选择CBO;通过alter session set optimizer_goal=all_rows 或 first_rows来选择CBO;通过添加hint来选择CBO;
(3)使用基于成本优化的一个关键是:存在表和索引的统计资料。通过analyze table 获得表的统计资料;通过analyze index获得索引的统计资料。
(4)对于超过5个表的连接的查询,建议不要使用成本优化器,而是在SQL语句中通过添加/* + rule */提示或者通过指定的执行计划来避免可能会在20分钟以上的SQL解析时间。
A.使用可声明的约束而不是通过代码限制
B、代码共享
共享的语句必须满足三个条件
:
A.
字符级的比较
:
当前被执行的语句和共享池中的语句必须完全相同。
B.
两个语句所指的对象必须完全相同
:(
同一用户下的对象
)
C.
两个
SQL
语句中必须使用相同的名字的绑定变量
(bindvariables)
C、使用绑定变量而不是文字来优化共享sql