在执行每一条sql的时候,oracle必须解析sql并且装载到高速缓存中,解析的过程包括很多内容,验证语法、检验提及的对象、确认用户权限等过程后还需要手机它所需要的所有信息,需要很多次的系统对象查询,所以说解析sql与不解析sql之间差别数十次的系统对象查询。
在高速缓存中存放的是解析完的sql的散列值,所以说每一次修改sql对oracle来说都是一个不同的sql,包括加注释,查看告诉缓存中的sql可以使用v$sql视图来查看(这里有一个技巧,为了方便查找我们可以给我们的sql定义一个唯一的id(注释)方便以后查询)。
SELECT /* key-menu */ * from ho_menu where name like '我的控制台';
select * from v$sql where sql_text like '%key-menu%';
块是oracle进行操作的最小单位,下面是只有一个查询块
select * from employees where department_id=1
而下面就是有两个查询块
select * from employees where department_id in (select department_id from departments)
查询块是通过select关键字来定义的,查询转换就是查询块的转换,在sql通过了语法与权限检查后就会进行查询转换,就是确定如果改变了查询写法会不会提供更好的查询计划,所以查询转换器可能会改变你所写的sql查询结构,但是不会影响最终的查询结果集。
最通常的转换就是将独立的查询快转换为直接连接:
select * from employees where department_id in (select department_id from departments)
转换后:
select * from employees e,departments d where e.department_id=d.department_id
你可以通过查看执行计划来了解是否发生了查询转换。
常用的几种转换能够引用到特定的查询中:
首先要知道的是解释计划与执行计划:
使用EXPLAIN PLAN FOR(也可以使用sqlplus的autotrance命令来执行,相关知识自查)来为一个查询生成预期的执行计划,也就是解释计划。
语法:
explain plan for sql;--生成信息并存到表plan_table中;
select * from table(dbms_xplan.display);
--dbms_xplan.display函数非常友好的基于每一条sql产生的计划自动显示适当的plan_table中的列。你可以自行查看plan_table中有那些列信息;
例子:
先执行:
explain plan for select * from HO_USER;
生成计划,然后执行下面的语句查询计划:
select * from table(dbms_xplan.display);
输出为(基本格式就是这样,稍微修正):
Plan hash value: 3700583471
Id | Operation | Name | Rows | Bytes | Cost (%CPU) | Time |
---|---|---|---|---|---|---|
0 | SELECT STATEMENT | 3 | 315 | 3 (0) | 00:00:01 | |
1 | TABLE ACCESS FULL | HO_USER | 3 | 315 | 3 (0) | 00:00:01 |
使用explain plan for来为一个查询生成预期的执行计划时输出包括下面几种:
下面列出一个稍微复杂的查询过程:
explain plan for select t.DEVICE_NAME,t.dep_code, d.NAME from JC_DEVICE_COLLECT t ,HO_DEPARTMENT d where t.DEP_CODE=d.CODE;.
select * from table(dbms_xplan.display);
结果:
Plan hash value: 3282717052
Id | Operation | Name | Rows | Bytes | Cost (%CPU) | Time |
---|---|---|---|---|---|---|
0 | SELECT STATEMENT | 20 | 1020 | 6 (17) | 00:00:01 | |
1 | MERGE JOIN | 20 | 1020 | 6 (17) | 00:00:01 | |
2 | TABLE ACCESS BY INDEX ROWID | HO_DEPARTMENT | 8 | 200 | 2 (0) | 00:00:01 |
3 | INDEX FULL SCAN | IDX_HO_DEPARTMENT_CODE | 8 | 1 (0) | 00:00:01 | |
|
SORT JOIN | 20 | 520 | 4 (25) | 00:00:01 | |
5 | TABLE ACCESS FULL | JC_DEVICE_COLLECT | 20 | 520 | 3 (0) | 00:00:01 |
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("T"."DEP_CODE"="D"."CODE")
filter("T"."DEP_CODE"="D"."CODE")
计划是按照顺序ID号来显示的,但是并不是严格按照自上而下来执行的,缩进最多的是执行过程中首先去执行的运算,同一层上的多个运算是自上而下依次执行,使用sqldeveloper能够很容易查看,并且直观显示出父子关系,子处理完交给父;
计划中最有用的部分之一就是谓语信息的部分,这个部分将会出现access predicates(访问谓语运算)、filter predicates(筛选谓语运算)
当sql语句执行的时候就会生成实际的执行计划,在sql被硬解析后所选的执行计划就会被存到库高速缓存备用,可以通过v$sql_plan来查看计划运算。
v$sql_plan与plan_table基本相同,知识添加了一些易于定位的列,比如sql_id、hash_value等,我们可以通过如下的方式查询执行计划:
select sql_id, sql_text from v$sql where sql_text like '%ho_menu%';--得到sql_id
select * from v$sql_plan where sql_id='ds8jqcuyaf6gu';--查询这条sql相关的计划
还有其它方式定位执行计划,我比较喜欢这种方式,还有更直观的是使用dbms_xplan.display_cursor(sql_id,child_number,format)函数;
select sql_id, sql_text from v$sql where sql_text like '%ho_menu%';--得到sql_id
select * from table(DBMS_XPLAN.DISPLAY_CURSOR('ds8jqcuyaf6gu',null));--没有值的使用默认值
这样查出来与解释计划格式类似,可以对比执行计划与解释计划是否一致。