ORACLE 面试问题-技术篇(1) | | ORACLE 面试问题-技术篇(1) 这也许是你一直期待的文章,在关注这部分技术问题的同时,请务必阅读有关面试中有关个人的问题和解答(这部分将在下几期中出现)。这里的回答并不是十分全面,这些问题可以通过多个角度来进行解释,也许你不必在面试过程中给出完全详尽的答案,只需要通过你的解答使面试考官了解你对ORACLE概念的熟悉程度。 1. 解释冷备份和热备份的不同点以及各自的优点 解答:热备份针对归档模式的数据库,在数据库仍旧处于工作状态时进行备份。而冷备份指在数据库关闭后,进行备份,适用于所有模式的数据库。热备份的优点在于当备份时,数据库仍旧可以被使用并且可以将数据库恢复到任意一个时间点。冷备份的优点在于它的备份和恢复操作相当简单,并且由于冷备份的数据库可以工作在非归档模式下,数据库性能会比归档模式稍好。(因为不必将archive log写入硬盘) 2. 你必须利用备份恢复数据库,但是你没有控制文件,该如何解决问题呢? 解答:重建控制文件,用带backup control file 子句的recover 命令恢复 数据库。 3. 如何转换init.ora到spfile? 解答:使用create spfile from pfile 命令 . 4. 解释data block , extent 和 segment的区别(这里建议用英文术语) 解答:data block是数据库中最小的逻辑存储单元。当数据库的对象需要更多的物理存储空间时,连续的data block就组成了extent . 一个数据库对象 拥有的所有extents被称为该对象的segment. 5. 给出两个检查表结构的方法 解答:1。DESCRIBE命令 2. DBMS_METADATA.GET_DDL 包 6. 怎样查看数据库引擎的报错 解答:alert log. 7. 比较truncate和delete 命令 解答:两者都可以用来删除表中所有的记录。区别在于:truncate是DDL操作,它移动HWK,不需要 rollback segment .而Delete是DML操作, 需要rollback segment 且花费较长时间. 8. 使用索引的理由 解答:快速访问表中的data block 9. 给出在STAR SCHEMA中的两种表及它们分别含有的数据 解答:Fact tables 和dimension tables. fact table 包含大量的主要的信息而 dimension tables 存放对fact table 某些属性描述的信息 10. FACT Table上需要建立何种索引? 解答:位图索引(bitmap index) 11. 给出两种相关约束? 解答:主键和外键 12. 如何在不影响子表的前提下,重建一个母表 解答:子表的外键强制实效,重建母表,激活外键 13. 解释归档和非归档模式之间的不同和它们各自的优缺点 解答:归档模式是指你可以备份所有的数据库 transactions并恢复到任意一个时间点。非归档模式则相反,不能恢复到任意一个时间点。但是非归档模式可以带来数据库性能上的少许提高 . 14. 如何建立一个备份控制文件? 解答:Alter database backup control file to trace. 15. 给出数据库正常启动所经历的几种状态 ? 解答: STARTUP NOMOUNT – 数据库实例启动 STARTUP MOUNT - 数据库装载 STARTUP OPEN – 数据库打开 16. 哪个column可以用来区别V$视图和GV$视图? 解答: INST_ID 指明集群环境中具体的某个instance 。 17. 如何生成explain plan? 解答: 运行utlxplan.sql. 建立plan 表 针对特定SQL语句,使用 explain plan set statement_id = 'tst1' into plan_table 运行utlxplp.sql 或 utlxpls.sql察看explain plan 18. 如何增加buffer cache的命中率? 解答:在数据库较繁忙时,适用buffer cache advisory 工具,查询v$db_cache_advice . 如果有必要更改,可以使用 alter system set db_cache_size 命令 19. ORA-01555的应对方法? 解答:具体的出错信息是snapshot too old within rollback seg , 通常可以通过 增大rollback seg来解决问题。当然也需要察看一下具体造成错误的SQL文本 20. 解释$ORACLE_HOME和$ORACLE_BASE的区别? 解答:ORACLE_BASE是oracle的根目录,ORACLE_HOME是oracle产品 的目录。 | | worldofpeppercrab[2005年 01月15日 21 : 25] 评论:[0] | 引用:[0] |
|
·从不sequential的sequence | | 从不sequential的sequence 遇到过好多问题关于如何在ORACLE 中创建类似SQLSERVER或ACCESS中自增长字段。答案多是先建立一个Sequence,然后在Trigger中将Sequence的NEXTVAL的取值赋予所需要的列。看上去还不错。 但是一切真的那么顺利吗?Sequence 真的可以做到提供一序列连续没有遗漏的序列数值吗? 不妨作个实验: SQL> create sequence test_seq start with 1;
Sequence created.
SQL> create table test_tab ( x int) ;
Table created.
SQL> insert into test_tab values (test_seq.nextval) ;
1 row created.
SQL> insert into test_tab values (test_seq.nextval) ;
1 row created.
SQL> insert into test_tab values (test_seq.nextval) ;
1 row created.
SQL> commit;
Commit complete.
SQL> select * from test_tab ;
X ---------- 1 2 3
SQL> conn / as sysdba; Connected. SQL> alter system flush shared_pool ;
System altered.
SQL> conn user1/user1 Connected. SQL> insert into test_tab values (test_seq.nextval) ;
1 row created.
SQL> commit;
Commit complete.
SQL> select * from test_tab ;
X ---------- 1 2 3 21 从试验中可以看出,在缺省情况下,我们建立的是带有Cache选项的Sequence (缺省值是20), 它的作用是预先将一定数量的序列值存放在SGA中,便于快速访问。可是它的副作用就是这部分数值可能会被清除, 当下一次获取NEXTVAL时,就会不可避免地造成序列值丢失。 总结一下,在以下情况下,序列值会丢失: 1. 数据库关闭或重起,由于整个SGA会被清除,所以Cached的序列值同样会被清除。 2. 类似于普通的Data Block ,当SGA中需要放置新的数据,Cached的序列值可能会按照SGA的数据存放规则被清除。 读到这里,细心的读者也许会问,如果在创建Sequence时,有意不选用Cache选项,问题不就解决了吗?且慢,还有两点需要注意: 1.访问效率降低,没有Cache功能的Sequence取值将无法直接访问内存 2.不论是Nocache还是Cache , 每次访问NEXTVAL的过程都是不可逆的,在同一session中,在执行一系列DML和Sequence的操作后,用户执行Rollback,希望将操作回滚,但是Sequence此时就显得异常顽固,用掉的NEXTVAL将无法被重现。当下一次试图读取NEXTVAL时,Sequence的指针又移动到下一位了。 看来Oracle真是一个海洋,每个细小的知识点都是那么饶有趣味,值得我们去努力专研啊。 备注:使用Cache功能对Sequence读取效率的影响 Connected to: Oracle8i Enterprise Edition Release 8.1.7.4.1 - Production With the Partitioning option JServer Release 8.1.7.4.1 - Production SQL> set timing on SQL> set autotrace traceonly statistics SQL> SELECT * FROM ALL_OBJECTS; 14302 rows selected. Elapsed: 00:00:13.05 Statistics ---------------------------------------------------------- 7 recursive calls 4 db block gets 146635 consistent gets 0 physical reads 0 redo size 1633344 bytes sent via SQL*Net to client 117520 bytes received via SQL*Net from client 956 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 14302 rows processed SQL> SQL> -- 测试带有CACHE选项的Sequence: SQL> SQL> CREATE SEQUENCE test_seq1 CACHE 1000; Sequence created. Elapsed: 00:00:00.00 SQL> SELECT x.*, test_seq1.NEXTVAL FROM ALL_OBJECTS x; 14303 rows selected. Elapsed: 00:00:13.09 Statistics ---------------------------------------------------------- 202 recursive calls 64 db block gets 146636 consistent gets 0 physical reads 10468 redo size 1752002 bytes sent via SQL*Net to client 117543 bytes received via SQL*Net from client 956 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 14303 rows processed SQL> SQL> --测试不带有CACHE选项的Sequence SQL> SQL> DROP SEQUENCE test_seq1; Sequence dropped. Elapsed: 00:00:00.00 SQL> CREATE SEQUENCE test_seq1 NOCACHE; Sequence created. Elapsed: 00:00:00.00 SQL> SELECT x.*, test_seq1.NEXTVAL FROM ALL_OBJECTS x; 14303 rows selected. Elapsed: 00:00:32.02 (执行时间明显长了) Statistics ---------------------------------------------------------- 185946 recursive calls 57216 db block gets 160925 consistent gets 0 physical reads 10004008 redo size 1752002 bytes sent via SQL*Net to client 117543 bytes received via SQL*Net from client 956 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 14303 rows processed | | worldofpeppercrab[2005年 01月15日 21 : 22] 评论:[0] | 引用:[0] |
|
·神秘的DUAL | | 神秘的DUAL
DUAL ? 有什么神秘的? 当你想得到ORACLE系统时间, 简简单单敲一行SQL 不就得了吗? 故弄玄虚…. SQL> select sysdate from dual; SYSDATE --------- 28-SEP-03 哈哈, 确实DUAL的使用很方便. 但是大家知道DUAL倒底是什么OBJECT, 它有什么特殊的行为吗? 来,我们一起看一看. 首先搞清楚DUAL是什么OBJECT : SQL> connect system/manager Connected. SQL> select owner, object_name , object_type from dba_objects where object_name like '%DUAL%'; OWNER OBJECT_NAME OBJECT_TYPE --------------- --------------- ------------- SYS DUAL TABLE PUBLIC DUAL SYNONYM 原来DUAL是属于SYS schema的一个表,然后以PUBLIC SYNONYM的方式供其他数据库USER使用. 再看看它的结构: SQL> desc dual Name Null? Type ----------------------------------------- -------- ---------------------------- DUMMY VARCHAR2(1) SQL> 只有一个名字叫DUMMY的字符型COLUMN . 然后查询一下表里的数据: SQL> select dummy from dual; DUMMY ---------- X 哦, 只有一条记录, DUMMY的值是’X’ .很正常啊,没什么奇怪嘛. 好,下面就有奇妙的东西出现了! 插入一条记录: SQL> connect sys as sysdba Connected. SQL> insert into dual values ( 'Y'); 1 row created. SQL> commit; Commit complete. SQL> select count(*) from dual; COUNT(*) ---------- 2 迄今为止,一切正常. 然而当我们再次查询记录时,奇怪的事情发生了 SQL> select * from dual; DUMMY ---------- X 刚才插入的那条记录并没有显示出来 ! 明明DUAL表中有两条记录, 可就是只显示一条! 再试一下删除 ,狠一点,全删光 ! SQL> delete from dual; /*注意没有限定条件,试图删除全部记录*/ 1 row deleted. SQL> commit; Commit complete. 哈哈,也只有一条记录被删掉, SQL> select * from dual; DUMMY ---------- Y 为什么会这样呢? 难道SQL的语法对DUAL不起作用吗?带着这个疑问, 我查询了一些ORACLE官方的资料. 原来ORACLE对DUAL表的操作做了一些内部处理,尽量保证DUAL表中只返回一条记录.当然这写内部操作是不可见的 . 看来ORACLE真是蕴藏着无穷的奥妙啊! 附: ORACLE关于DUAL表不同寻常特性的解释 There is internalized code that makes this happen. Code checks that ensure that a table scan of SYS.DUAL only returns one row. Svrmgrl behaviour is incorrect but this is now an obsolete product. The base issue you should always remember and keep is: DUAL table should always have 1 ROW. Dual is a normal table with one dummy column of varchar2(1). This is basically used from several applications as a pseudo table for getting results from a select statement that use functions like sysdate or other prebuilt or application functions. If DUAL has no rows at all some applications (that use DUAL) may fail with NO_DATA_FOUND exception. If DUAL has more than 1 row then applications (that use DUAL) may fail with TOO_MANY_ROWS exception. So DUAL should ALWAYS have 1 and only 1 row | | worldofpeppercrab[2005年 01月15日 21 : 22] 评论:[0] | 引用:[1] |
|
·先排序还是先取值 | | 先排序还是先取值
题目: MS SQLSERVER和ORACLE中取出表中按照某字段排序的前N条记录 这个题目看上去似乎那么简单, 两种数据库都提供ORDER BY 子句. 问题应该能够迎刃而解吧. 先试一下MS SQLSERVER是怎么做的: use Northwind; create table TestSort (ID integer); insert into testSort values (3); insert into testSort values (1); insert into testSort values (4); insert into testSort values (2); select * from testSort; ----------------------------------------- ID ----------- 3 1 4 2 (4 row(s) affected) 假设我们要取出按照ID排序的前三条记录: select TOP 3 * from testSort order by ID ; ----------------------------------------- ID ----------- 1 2 3 (3 row(s) affected) 很简单,一句话就解决了. 再试一下ORACLE (这里用ORACLE9i) SQL> create table TestSort ( ID number); Table created. SQL> insert into testSort values (3); 1 row created. SQL> insert into testSort values (1); 1 row created. SQL> insert into testSort values (4); 1 row created. SQL> insert into testSort values (2); 1 row created. SQL> commit; Commit complete. SQL> select * from testSort; ID ---------- 3 1 4 2 ORACLE没有MS SQLSERVER中取前N条记录的TOP语法. 但是有ROWNUM可以用来完成类似功能. SQL> select * from TestSort where rownum <= 3 order by ID; ID ---------- 1 3 4 结果是不是有点出乎意料? 它并没有返回所要求的 1 , 2 , 3的结果 . ORACLE先根据rownum <=3的条件限制选取一个范围集合(3,1,4), 然后再在这个集合里进行排序. ORDER BY 子句是在合适的记录被取出后才起作用. 原来如此, 那么在ORACLE中如何才能实现这个功能呢? 通常我们可以采用这种办法: SQL> select * from (select * from TestSort order by ID) where rownum <=3; ID ---------- 1 2 3 有点麻烦,不过也只能这样. 相同道理, 如果想从表中取出按照某字段排序前M到N条记录 下面的ORACLE语句是最好的: SQL> select ID from ( select ID , rownum as con from ( select ID from TestSort order by ID ) where rownum <= 3 /*N值*/ ) where con >= 2; /*M 值*/ ID ---------- 2 3 MS SQLSERVER中也可以用类似的思路解决此类问题. 当然你也可以用笨一点的办法, 比如用前N条记录的集合MINUS前M-1条记录的集合得到 前M到N条记录集合. (有点象饶口令) , 不过MS SQLSERVER好象不支持MINUS之类的集合操作. 看来小小的一个排序取值问题还真不是那么简单呢 ! | | worldofpeppercrab[2005年 01月15日 21 : 21] 评论:[0] | 引用:[0] |
|
·ORACLE SQL性能优化系列 (十四) 完结篇 | | ORACLE SQL性能优化系列 (十四) 完结篇
46. 连接多个扫描 如果你对一个列和一组有限的值进行比较, 优化器可能执行多次扫描并对结果进行合并连接. 举例: SELECT * FROM LODGING WHERE MANAGER IN (‘BILL GATES’,’KEN MULLER’); 优化器可能将它转换成以下形式 SELECT * FROM LODGING WHERE MANAGER = ‘BILL GATES’ OR MANAGER = ’KEN MULLER’; 当选择执行路径时, 优化器可能对每个条件采用LODGING$MANAGER上的索引范围扫描. 返回的ROWID用来访问LODGING表的记录 (通过TABLE ACCESS BY ROWID 的方式). 最后两组记录以连接(CONCATENATION)的形式被组合成一个单一的集合. Explain Plan : SELECT STATEMENT Optimizer=CHOOSE CONCATENATION TABLE ACCESS (BY INDEX ROWID) OF LODGING INDEX (RANGE SCAN ) OF LODGING$MANAGER (NON-UNIQUE) TABLE ACCESS (BY INDEX ROWID) OF LODGING INDEX (RANGE SCAN ) OF LODGING$MANAGER (NON-UNIQUE) 译者按: 本节和第37节似乎有矛盾之处. 47. CBO下使用更具选择性的索引 基于成本的优化器(CBO, Cost-Based Optimizer)对索引的选择性进行判断来决定索引的使用是否能提高效率. 如果索引有很高的选择性, 那就是说对于每个不重复的索引键值,只对应数量很少的记录. 比如, 表中共有100条记录而其中有80个不重复的索引键值. 这个索引的选择性就是80/100 = 0.8 . 选择性越高, 通过索引键值检索出的记录就越少. 如果索引的选择性很低, 检索数据就需要大量的索引范围查询操作和ROWID 访问表的 操作. 也许会比全表扫描的效率更低. 译者按: 下列经验请参阅: a. 如果检索数据量超过30%的表中记录数.使用索引将没有显著的效率提高. b. 在特定情况下, 使用索引也许会比全表扫描慢, 但这是同一个数量级上的 区别. 而通常情况下,使用索引比全表扫描要块几倍乃至几千倍! 48. 避免使用耗费资源的操作 带有DISTINCT,UNION,MINUS,INTERSECT,ORDER BY的SQL语句会启动SQL引擎 执行耗费资源的排序(SORT)功能. DISTINCT需要一次排序操作, 而其他的至少需要执行两次排序. 例如,一个UNION查询,其中每个查询都带有GROUP BY子句, GROUP BY会触发嵌入排序(NESTED SORT) ; 这样, 每个查询需要执行一次排序, 然后在执行UNION时, 又一个唯一排序(SORT UNIQUE)操作被执行而且它只能在前面的嵌入排序结束后才能开始执行. 嵌入的排序的深度会大大影响查询的效率. 通常, 带有UNION, MINUS , INTERSECT的SQL语句都可以用其他方式重写. 译者按: 如果你的数据库的SORT_AREA_SIZE调配得好, 使用UNION , MINUS, INTERSECT也是可以考虑的, 毕竟它们的可读性很强 49. 优化GROUP BY 提高GROUP BY 语句的效率, 可以通过将不需要的记录在GROUP BY 之前过滤掉.下面两个查询返回相同结果但第二个明显就快了许多. 低效: SELECT JOB , AVG(SAL) FROM EMP GROUP JOB HAVING JOB = ‘PRESIDENT’ OR JOB = ‘MANAGER’ 高效: SELECT JOB , AVG(SAL) FROM EMP WHERE JOB = ‘PRESIDENT’ OR JOB = ‘MANAGER’ GROUP JOB 译者按: 本节和14节相同. 可略过. 50. 使用日期 当使用日期是,需要注意如果有超过5位小数加到日期上, 这个日期会进到下一天! 例如: 1. SELECT TO_DATE(‘01-JAN-93’+.99999) FROM DUAL; Returns: ’01-JAN-93 23:59:59’ 2. SELECT TO_DATE(‘01-JAN-93’+.999999) FROM DUAL; Returns: ’02-JAN-93 00:00:00’ 译者按: 虽然本节和SQL性能优化没有关系, 但是作者的功力可见一斑 51. 使用显式的游标(CURSORs) 使用隐式的游标,将会执行两次操作. 第一次检索记录, 第二次检查TOO MANY ROWS 这个exception . 而显式游标不执行第二次操作. 52. 优化EXPORT和IMPORT 使用较大的BUFFER(比如10MB , 10,240,000)可以提高EXPORT和IMPORT的速度. ORACLE将尽可能地获取你所指定的内存大小,即使在内存不满足,也不会报错.这个值至少要和表中最大的列相当,否则列值会被截断. 译者按: 可以肯定的是, 增加BUFFER会大大提高EXPORT , IMPORT的效率. (曾经碰到过一个CASE, 增加BUFFER后,IMPORT/EXPORT快了10倍!) 作者可能犯了一个错误: “这个值至少要和表中最大的列相当,否则列值会被截断. “ 其中最大的列也许是指最大的记录大小. 关于EXPORT/IMPORT的优化,CSDN论坛中有一些总结性的贴子,比如关于BUFFER参数, COMMIT参数等等, 详情请查. 53. 分离表和索引 总是将你的表和索引建立在不同的表空间内(TABLESPACES). 决不要将不属于ORACLE内部系统的对象存放到SYSTEM表空间里. 同时,确保数据表空间和索引表空间置于不同的硬盘上. 译者按: “同时,确保数据表空间和索引表空间置与不同的硬盘上.”可能改为如下更为准确 “同时,确保数据表空间和索引表空间置与不同的硬盘控制卡控制的硬盘上.” (全文完) Black_Snail ligang1000@hotmail.com peppercrab@gmail.com 4/Sep/2003 | | worldofpeppercrab[2005年 01月15日 21 : 20] 评论:[0] | 引用:[0] |
|
·ORACLE SQL性能优化系列 (十三) | | ORACLE SQL性能优化系列 (十三)
43. 用WHERE替代ORDER BY ORDER BY 子句只在两种严格的条件下使用索引. ORDER BY中所有的列必须包含在相同的索引中并保持在索引中的排列顺序. ORDER BY中所有的列必须定义为非空. WHERE子句使用的索引和ORDER BY子句中所使用的索引不能并列. 例如: 表DEPT包含以下列: DEPT_CODE PK NOT NULL DEPT_DESC NOT NULL DEPT_TYPE NULL 非唯一性的索引(DEPT_TYPE) 低效: (索引不被使用) SELECT DEPT_CODE FROM DEPT ORDER BY DEPT_TYPE EXPLAIN PLAN: SORT ORDER BY TABLE ACCESS FULL 高效: (使用索引) SELECT DEPT_CODE FROM DEPT WHERE DEPT_TYPE > 0 EXPLAIN PLAN: TABLE ACCESS BY ROWID ON EMP INDEX RANGE SCAN ON DEPT_IDX 译者按: ORDER BY 也能使用索引! 这的确是个容易被忽视的知识点. 我们来验证一下: SQL> select * from emp order by empno; Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE 1 0 TABLE ACCESS (BY INDEX ROWID) OF 'EMP' 2 1 INDEX (FULL SCAN) OF 'EMPNO' (UNIQUE) 44. 避免改变索引列的类型. 当比较不同数据类型的数据时, ORACLE自动对列进行简单的类型转换. 假设 EMPNO是一个数值类型的索引列. SELECT … FROM EMP WHERE EMPNO = ‘123’ 实际上,经过ORACLE类型转换, 语句转化为: SELECT … FROM EMP WHERE EMPNO = TO_NUMBER(‘123’) 幸运的是,类型转换没有发生在索引列上,索引的用途没有被改变. 现在,假设EMP_TYPE是一个字符类型的索引列. SELECT … FROM EMP WHERE EMP_TYPE = 123 这个语句被ORACLE转换为: SELECT … FROM EMP WHERE TO_NUMBER(EMP_TYPE)=123 因为内部发生的类型转换, 这个索引将不会被用到! 译者按: 为了避免ORACLE对你的SQL进行隐式的类型转换, 最好把类型转换用显式表现出来. 注意当字符和数值比较时, ORACLE会优先转换数值类型到字符类型. 45. 需要当心的WHERE子句 某些SELECT 语句中的WHERE子句不使用索引. 这里有一些例子. 在下面的例子里, ‘!=’ 将不使用索引. 记住, 索引只能告诉你什么存在于表中, 而不能告诉你什么不存在于表中. 不使用索引: SELECT ACCOUNT_NAME FROM TRANSACTION WHERE AMOUNT !=0; 使用索引: SELECT ACCOUNT_NAME FROM TRANSACTION WHERE AMOUNT >0; 下面的例子中, ‘||’是字符连接函数. 就象其他函数那样, 停用了索引. 不使用索引: 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’; 下面的例子中, ‘+’是数学函数. 就象其他数学函数那样, 停用了索引. 不使用索引: SELECT ACCOUNT_NAME, AMOUNT FROM TRANSACTION WHERE AMOUNT + 3000 >5000; 使用索引: SELECT ACCOUNT_NAME, AMOUNT FROM TRANSACTION WHERE AMOUNT > 2000 ; 下面的例子中,相同的索引列不能互相比较,这将会启用全表扫描. 不使用索引: SELECT ACCOUNT_NAME, AMOUNT FROM TRANSACTION WHERE ACCOUNT_NAME = NVL(:ACC_NAME,ACCOUNT_NAME); 使用索引: SELECT ACCOUNT_NAME, AMOUNT FROM TRANSACTION WHERE ACCOUNT_NAME LIKE NVL(:ACC_NAME,’%’); 译者按: 如果一定要对使用函数的列启用索引, ORACLE新的功能: 基于函数的索引(Function-Based Index) 也许是一个较好的方案. CREATE INDEX EMP_I ON EMP (UPPER(ename)); /*建立基于函数的索引*/ SELECT * FROM emp WHERE UPPER(ename) = ‘BLACKSNAIL’; /*将使用索引*/
| | worldofpeppercrab[2005年 01月15日 21 : 18] 评论:[0] | 引用:[0] |
|
·ORACLE SQL性能优化系列 (十二) | | ORACLE SQL性能优化系列 (十二)
39. 总是使用索引的第一个列 如果索引是建立在多个列上, 只有在它的第一个列(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' 很明显, 当仅引用索引的第二个列时,优化器使用了全表扫描而忽略了索引 40. ORACLE内部操作 当执行查询时,ORACLE采用了内部的操作. 下表显示了几种重要的内部操作. ORACLE Clause | 内部操作 | ORDER BY | SORT ORDER BY | UNION | UNION-ALL | MINUS | MINUS | INTERSECT | INTERSECT | DISTINCT,MINUS,INTERSECT,UNION | SORT UNIQUE | MIN,MAX,COUNT | SORT AGGREGATE | GROUP BY | SORT GROUP BY | ROWNUM | COUNT or COUNT STOPKEY | Queries involving Joins | SORT JOIN,MERGE JOIN,NESTED LOOPS | CONNECT BY | CONNECT BY |
41. 用UNION-ALL 替换UNION ( 如果有可能的话) 当SQL语句需要UNION两个查询结果集合时,这两个结果集合会以UNION-ALL的方式被合并, 然后在输出最终结果前进行排序. 如果用UNION ALL替代UNION, 这样排序就不是必要了. 效率就会因此得到提高. 举例: 低效: SELECT ACCT_NUM, BALANCE_AMT FROM DEBIT_TRANSACTIONS WHERE TRAN_DATE = ’31-DEC-95’ UNION SELECT ACCT_NUM, BALANCE_AMT FROM DEBIT_TRANSACTIONS WHERE TRAN_DATE = ’31-DEC-95’ 高效: SELECT ACCT_NUM, BALANCE_AMT FROM DEBIT_TRANSACTIONS WHERE TRAN_DATE = ’31-DEC-95’ UNION ALL SELECT ACCT_NUM, BALANCE_AMT FROM DEBIT_TRANSACTIONS WHERE TRAN_DATE = ’31-DEC-95’ 译者按: 需要注意的是,UNION ALL 将重复输出两个结果集合中相同记录. 因此各位还是 要从业务需求分析使用UNION ALL的可行性. UNION 将对结果集合排序,这个操作会使用到SORT_AREA_SIZE这块内存. 对于这 块内存的优化也是相当重要的. 下面的SQL可以用来查询排序的消耗量 Select substr(name,1,25) "Sort Area Name", substr(value,1,15) "Value" from v$sysstat where name like 'sort%' 42. 使用提示(Hints) 对于表的访问,可以使用两种Hints. FULL 和 ROWID FULL hint 告诉ORACLE使用全表扫描的方式访问指定表. 例如: SELECT /*+ FULL(EMP) */ * FROM EMP WHERE EMPNO = 7893; ROWID hint 告诉ORACLE使用TABLE ACCESS BY ROWID的操作访问表. 通常, 你需要采用TABLE ACCESS BY ROWID的方式特别是当访问大表的时候, 使用这种方式, 你需要知道ROIWD的值或者使用索引. 如果一个大表没有被设定为缓存(CACHED)表而你希望它的数据在查询结束是仍然停留 在SGA中,你就可以使用CACHE hint 来告诉优化器把数据保留在SGA中. 通常CACHE hint 和 FULL hint 一起使用. 例如: SELECT /*+ FULL(WORKER) CACHE(WORKER)*/ * FROM WORK; 索引hint 告诉ORACLE使用基于索引的扫描方式. 你不必说明具体的索引名称 例如: SELECT /*+ INDEX(LODGING) */ LODGING FROM LODGING WHERE MANAGER = ‘BILL GATES’; 在不使用hint的情况下, 以上的查询应该也会使用索引,然而,如果该索引的重复值过多而你的优化器是CBO, 优化器就可能忽略索引. 在这种情况下, 你可以用INDEX hint强制ORACLE使用该索引. ORACLE hints 还包括ALL_ROWS, FIRST_ROWS, RULE,USE_NL, USE_MERGE, USE_HASH 等等. 译者按: 使用hint , 表示我们对ORACLE优化器缺省的执行路径不满意,需要手工修改. 这是一个很有技巧性的工作. 我建议只针对特定的,少数的SQL进行hint的优化. 对ORACLE的优化器还是要有信心(特别是CBO)
| | worldofpeppercrab[2005年 01月15日 21 : 18] 评论:[0] | 引用:[0] |
|
·ORACLE SQL性能优化系列 (十一) | | ORACLE SQL性能优化系列 (十一)
36. 用UNION替换OR (适用于索引列) 通常情况下, 用UNION替换WHERE子句中的OR将会起到较好的效果. 对索引列使用OR将造成全表扫描. 注意, 以上规则只针对多个索引列有效. 如果有column没有被索引, 查询效率可能会因为你没有选择OR而降低. 在下面的例子中, LOC_ID 和REGION上都建有索引. 高效: SELECT LOC_ID , LOC_DESC , REGION FROM LOCATION WHERE LOC_ID = 10 UNION SELECT LOC_ID , LOC_DESC , REGION FROM LOCATION WHERE REGION = “MELBOURNE” 低效: SELECT LOC_ID , LOC_DESC , REGION FROM LOCATION WHERE LOC_ID = 10 OR REGION = “MELBOURNE” 如果你坚持要用OR, 那就需要返回记录最少的索引列写在最前面. 注意: WHERE KEY1 = 10 (返回最少记录) OR KEY2 = 20 (返回最多记录) ORACLE 内部将以上转换为 WHERE KEY1 = 10 AND ((NOT KEY1 = 10) AND KEY2 = 20) 译者按: 下面的测试数据仅供参考: (a = 1003 返回一条记录 , b = 1 返回1003条记录) SQL> select * from unionvsor /*1st test*/ 2 where a = 1003 or b = 1; 1003 rows selected. Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE 1 0 CONCATENATION 2 1 TABLE ACCESS (BY INDEX ROWID) OF 'UNIONVSOR' 3 2 INDEX (RANGE SCAN) OF 'UB' (NON-UNIQUE) 4 1 TABLE ACCESS (BY INDEX ROWID) OF 'UNIONVSOR' 5 4 INDEX (RANGE SCAN) OF 'UA' (NON-UNIQUE) Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 144 consistent gets 0 physical reads 0 redo size 63749 bytes sent via SQL*Net to client 7751 bytes received via SQL*Net from client 68 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1003 rows processed SQL> select * from unionvsor /*2nd test*/ 2 where b = 1 or a = 1003 ; 1003 rows selected. Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE 1 0 CONCATENATION 2 1 TABLE ACCESS (BY INDEX ROWID) OF 'UNIONVSOR' 3 2 INDEX (RANGE SCAN) OF 'UA' (NON-UNIQUE) 4 1 TABLE ACCESS (BY INDEX ROWID) OF 'UNIONVSOR' 5 4 INDEX (RANGE SCAN) OF 'UB' (NON-UNIQUE) Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 143 consistent gets 0 physical reads 0 redo size 63749 bytes sent via SQL*Net to client 7751 bytes received via SQL*Net from client 68 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1003 rows processed SQL> select * from unionvsor /*3rd test*/ 2 where a = 1003 3 union 4 select * from unionvsor 5 where b = 1; 1003 rows selected. Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE 1 0 SORT (UNIQUE) 2 1 UNION-ALL 3 2 TABLE ACCESS (BY INDEX ROWID) 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 calls 0 db block gets 10 consistent gets 0 physical reads 0 redo size 63735 bytes sent via SQL*Net to client 7751 bytes received via SQL*Net from client 68 SQL*Net roundtrips to/from client 1 sorts (memory) 0 sorts (disk) 1003 rows processed 用UNION的效果可以从consistent gets和 SQL*NET的数据交换量的减少看出 37. 用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); 译者按: 这是一条简单易记的规则,但是实际的执行效果还须检验,在ORACLE8i下,两者的执行路径似乎是相同的. 38. 避免在索引列上使用IS NULL和IS NOT NULL 避免在索引中使用任何可以为空的列,ORACLE将无法使用该索引.对于单列索引,如果列包含空值,索引中将不存在此记录. 对于复合索引,如果每个列都为空,索引中同样不存在此记录. 如果至少有一个列不为空,则记录存在于索引中. 举例: 如果唯一性索引建立在表的A列和B列上, 并且表中存在一条记录的A,B值为(123,null) , ORACLE将不接受下一条具有相同A,B值(123,null)的记录(插入). 然而如果 所有的索引列都为空,ORACLE将认为整个键值为空而空不等于空. 因此你可以插入1000 条具有相同键值的记录,当然它们都是空! 因为空值不存在于索引列中,所以WHERE子句中对索引列进行空值比较将使ORACLE停用该索引. 举例: 低效: (索引失效) SELECT … FROM DEPARTMENT WHERE DEPT_CODE IS NOT NULL; 高效: (索引有效) SELECT … FROM DEPARTMENT WHERE DEPT_CODE >=0;
| | worldofpeppercrab[2005年 01月15日 21 : 14] 评论:[0] | 引用:[0] |
|
·ORACLE SQL性能优化系列 (十) | | ORACLE SQL性能优化系列 (十)
31. 强制索引失效 如果两个或以上索引具有相同的等级,你可以强制命令ORACLE优化器使用其中的一个(通过它,检索出的记录数量少) . 举例: SELECT ENAME FROM EMP WHERE EMPNO = 7935 AND DEPTNO + 0 = 10 /*DEPTNO上的索引将失效*/ AND EMP_TYPE || ‘’ = ‘A’ /*EMP_TYPE上的索引将失效*/ 这是一种相当直接的提高查询效率的办法. 但是你必须谨慎考虑这种策略,一般来说,只有在你希望单独优化几个SQL时才能采用它. 这里有一个例子关于何时采用这种策略, 假设在EMP表的EMP_TYPE列上有一个非唯一性的索引而EMP_CLASS上没有索引. SELECT ENAME FROM EMP WHERE EMP_TYPE = ‘A’ AND EMP_CLASS = ‘X’; 优化器会注意到EMP_TYPE上的索引并使用它. 这是目前唯一的选择. 如果,一段时间以后, 另一个非唯一性建立在EMP_CLASS上,优化器必须对两个索引进行选择,在通常情况下,优化器将使用两个索引并在他们的结果集合上执行排序及合并. 然而,如果其中一个索引(EMP_TYPE)接近于唯一性而另一个索引(EMP_CLASS)上有几千个重复的值. 排序及合并就会成为一种不必要的负担. 在这种情况下,你希望使优化器屏蔽掉EMP_CLASS索引. 用下面的方案就可以解决问题. SELECT ENAME FROM EMP WHERE EMP_TYPE = ‘A’ AND EMP_CLASS||’’ = ‘X’; 32. 避免在索引列上使用计算. WHERE子句中,如果索引列是函数的一部分.优化器将不使用索引而使用全表扫描. 举例: 低效: SELECT … FROM DEPT WHERE SAL * 12 > 25000; 高效: SELECT … FROM DEPT WHERE SAL > 25000/12; 译者按: 这是一个非常实用的规则,请务必牢记 33. 自动选择索引 如果表中有两个以上(包括两个)索引,其中有一个唯一性索引,而其他是非唯一性. 在这种情况下,ORACLE将使用唯一性索引而完全忽略非唯一性索引. 举例: SELECT ENAME FROM EMP WHERE EMPNO = 2326 AND DEPTNO = 20 ; 这里,只有EMPNO上的索引是唯一性的,所以EMPNO索引将用来检索记录. TABLE ACCESS BY ROWID ON EMP INDEX UNIQUE SCAN ON EMP_NO_IDX 34. 避免在索引列上使用NOT 通常, 我们要避免在索引列上使用NOT, NOT会产生在和在索引列上使用函数相同的 影响. 当ORACLE”遇到”NOT,他就会停止使用索引转而执行全表扫描. 举例: 低效: (这里,不使用索引) SELECT … FROM DEPT WHERE DEPT_CODE NOT = 0; 高效: (这里,使用了索引) SELECT … FROM DEPT WHERE DEPT_CODE > 0; 需要注意的是,在某些时候, ORACLE优化器会自动将NOT转化成相对应的关系操作符. NOT > to <= NOT >= to < NOT < to >= NOT <= to > 译者按: 在这个例子中,作者犯了一些错误. 例子中的低效率SQL是不能被执行的. 我做了一些测试: SQL> select * from emp where NOT empno > 1; no rows selected Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE 1 0 TABLE ACCESS (BY INDEX ROWID) OF 'EMP' 2 1 INDEX (RANGE SCAN) OF 'EMPNO' (UNIQUE) SQL> select * from emp where empno <= 1; no rows selected Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE 1 0 TABLE ACCESS (BY INDEX ROWID) OF 'EMP' 2 1 INDEX (RANGE SCAN) OF 'EMPNO' (UNIQUE) 两者的效率完全一样,也许这符合作者关于” 在某些时候, ORACLE优化器会自动将NOT转化成相对应的关系操作符” 的观点. 35. 用>=替代> 如果DEPTNO上有一个索引, 高效: SELECT * FROM EMP WHERE DEPTNO >=4 低效: SELECT * FROM EMP WHERE DEPTNO >3 两者的区别在于, 前者DBMS将直接跳到第一个DEPT等于4的记录而后者将首先定位到DEPTNO=3的记录并且向前扫描到第一个DEPT大于3的记录. | | worldofpeppercrab[2005年 01月15日 21 : 13] 评论:[0] | 引用:[0] |
|
·ORACLE SQL性能优化系列 (九) | | ORACLE SQL性能优化系列 (九)
27. 基础表的选择 基础表(Driving Table)是指被最先访问的表(通常以全表扫描的方式被访问). 根据优化器的不同, SQL语句中基础表的选择是不一样的. 如果你使用的是CBO (COST BASED OPTIMIZER),优化器会检查SQL语句中的每个表的物理大小,索引的状态,然后选用花费最低的执行路径. 如果你用RBO (RULE BASED OPTIMIZER) , 并且所有的连接条件都有索引对应, 在这种情况下, 基础表就是FROM 子句中列在最后的那个表. 举例: SELECT A.NAME , B.MANAGER FROM WORKER A, LODGING B WHERE A.LODGING = B.LODING; 由于LODGING表的LODING列上有一个索引, 而且WORKER表中没有相比较的索引, WORKER表将被作为查询中的基础表. 28. 多个平等的索引 当SQL语句的执行路径可以使用分布在多个表上的多个索引时, ORACLE会同时使用多个索引并在运行时对它们的记录进行合并, 检索出仅对全部索引有效的记录. 在ORACLE选择执行路径时,唯一性索引的等级高于非唯一性索引. 然而这个规则只有 当WHERE子句中索引列和常量比较才有效.如果索引列和其他表的索引类相比较. 这种子句在优化器中的等级是非常低的. 如果不同表中两个想同等级的索引将被引用, FROM子句中表的顺序将决定哪个会被率先使用. FROM子句中最后的表的索引将有最高的优先级. 如果相同表中两个想同等级的索引将被引用, WHERE子句中最先被引用的索引将有最高的优先级. 举例: DEPTNO上有一个非唯一性索引,EMP_CAT也有一个非唯一性索引. SELECT ENAME, FROM EMP WHERE DEPT_NO = 20 AND EMP_CAT = ‘A’; 这里,DEPTNO索引将被最先检索,然后同EMP_CAT索引检索出的记录进行合并. 执行路径如下: TABLE ACCESS BY ROWID ON EMP AND-EQUAL INDEX RANGE SCAN ON DEPT_IDX INDEX RANGE SCAN ON CAT_IDX 29. 等式比较和范围比较 当WHERE子句中有索引列, ORACLE不能合并它们,ORACLE将用范围比较. 举例: DEPTNO上有一个非唯一性索引,EMP_CAT也有一个非唯一性索引. SELECT ENAME FROM EMP WHERE DEPTNO > 20 AND EMP_CAT = ‘A’; 这里只有EMP_CAT索引被用到,然后所有的记录将逐条与DEPTNO条件进行比较. 执行路径如下: TABLE ACCESS BY ROWID ON EMP INDEX RANGE SCAN ON CAT_IDX 30. 不明确的索引等级 当ORACLE无法判断索引的等级高低差别,优化器将只使用一个索引,它就是在WHERE子句中被列在最前面的. 举例: DEPTNO上有一个非唯一性索引,EMP_CAT也有一个非唯一性索引. SELECT ENAME FROM EMP WHERE DEPTNO > 20 AND EMP_CAT > ‘A’; 这里, ORACLE只用到了DEPT_NO索引. 执行路径如下: TABLE ACCESS BY ROWID ON EMP INDEX RANGE SCAN ON DEPT_IDX 译者按: 我们来试一下以下这种情况: SQL> select index_name, uniqueness from user_indexes where table_name = 'EMP'; INDEX_NAME UNIQUENES ------------------------------ --------- EMPNO UNIQUE EMPTYPE NONUNIQUE SQL> select * from emp where empno >= 2 and emp_type = 'A' ; no rows selected Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE 1 0 TABLE ACCESS (BY INDEX ROWID) OF 'EMP' 2 1 INDEX (RANGE SCAN) OF 'EMPTYPE' (NON-UNIQUE) 虽然EMPNO是唯一性索引,但是由于它所做的是范围比较, 等级要比非唯一性索引的等式比较低! | | worldofpeppercrab[2005年 01月15日 21 : 12] 评论:[0] | 引用:[0] |
|
·ORACLE SQL性能优化系列 (八) | | ORACLE SQL性能优化系列 (八)
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. 索引的操作 ORACLE对索引有两种访问模式. 索引唯一扫描 ( INDEX UNIQUE SCAN) 大多数情况下, 优化器通过WHERE子句访问INDEX. 例如: 表LODGING有两个索引 : 建立在LODGING列上的唯一性索引LODGING_PK和建立在MANAGER列上的非唯一性索引LODGING$MANAGER. SELECT * FROM LODGING WHERE LODGING = ‘ROSE HILL’; 在内部 , 上述SQL将被分成两步执行, 首先 , LODGING_PK 索引将通过索引唯一扫描的方式被访问 , 获得相对应的ROWID, 通过ROWID访问表的方式执行下一步检索. 如果被检索返回的列包括在INDEX列中,ORACLE将不执行第二步的处理(通过ROWID访问表). 因为检索数据保存在索引中, 单单访问索引就可以完全满足查询结果. 下面SQL只需要INDEX UNIQUE SCAN 操作. SELECT LODGING FROM LODGING WHERE LODGING = ‘ROSE HILL’; 索引范围查询(INDEX RANGE SCAN) 适用于两种情况: 1. 基于一个范围的检索 2. 基于非唯一性索引的检索 例1: SELECT LODGING FROM LODGING WHERE LODGING LIKE ‘M%’; WHERE子句条件包括一系列值, ORACLE将通过索引范围查询的方式查询LODGING_PK . 由于索引范围查询将返回一组值, 它的效率就要比索引唯一扫描 低一些. 例2: SELECT LODGING FROM LODGING WHERE MANAGER = ‘BILL GATES’; 这个SQL的执行分两步, LODGING$MANAGER的索引范围查询(得到所有符合条件记录的ROWID) 和下一步同过ROWID访问表得到LODGING列的值. 由于LODGING$MANAGER是一个非唯一性的索引,数据库不能对它执行索引唯一扫描. 由于SQL返回LODGING列,而它并不存在于LODGING$MANAGER索引中, 所以在索引范围查询后会执行一个通过ROWID访问表的操作. WHERE子句中, 如果索引列所对应的值的第一个字符由通配符(WILDCARD)开始, 索引将不被采用. SELECT LODGING FROM LODGING WHERE MANAGER LIKE ‘%HANMAN’; 在这种情况下,ORACLE将使用全表扫描 | | worldofpeppercrab[2005年 01月15日 21 : 11] 评论:[0] | 引用:[0] |
|
·ORACLE SQL性能优化系列 (七) | | ORACLE SQL性能优化系列 (七)
24. 用EXPLAIN PLAN 分析SQL语句 EXPLAIN PLAN 是一个很好的分析SQL语句的工具,它甚至可以在不执行SQL的情况下分析语句. 通过分析,我们就可以知道ORACLE是怎么样连接表,使用什么方式扫描表(索引扫描或全表扫描)以及使用到的索引名称. 你需要按照从里到外,从上到下的次序解读分析的结果. EXPLAIN PLAN分析的结果是用缩进的格式排列的, 最内部的操作将被最先解读, 如果两个操作处于同一层中,带有最小操作号的将被首先执行. NESTED LOOP是少数不按照上述规则处理的操作, 正确的执行路径是检查对NESTED LOOP提供数据的操作,其中操作号最小的将被最先处理. 译者按: 通过实践, 感到还是用SQLPLUS中的SET TRACE 功能比较方便. 举例: 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) 注: 目前许多第三方的工具如TOAD和ORACLE本身提供的工具如OMS的SQL Analyze都提供了极其方便的EXPLAIN PLAN工具.也许喜欢图形化界面的朋友们可以选用它们. |
|