sql 被保存在 share pool 后, 开始解析, 解析包括语句的语法, 检验及对象, 以确认该用于是否有该对象的权限, 如果这些都通过了, 接下来就要看这个语句之前是否被执行过, 如果是, oracle 将取回之前解析的信息并重用, 这就是软解析, 如果没有被执行过, 那么oracle就将执行所有的工作来为当前语句生成执行计划, 并将它存在于缓存中以便将来使用, 这就是硬解析.
硬解析需要做很多工作, 如果想看硬解析都干了什么, 最简单的办法是打开扩展SQL追踪, 执行一个语句然后查询追踪数据.
可以看出最好是每个 sql 语句都用软解析, 查询 v$sql 区域查看执行过的 sql 语句, 例如
1: select * from employees where department_id = 60;
2: SELECT * FROM EMPLOYEES WHERE DEPARTMENT_ID = 60;
3: select /* a_comment */ * from employees where department_id = 60;
上述3条语句, 执行结果完全相同, 但是他们却是不相同的SQL语句, 不能实现软解析. 因为 在执行一条语句时, oracle会首先将字符串转换成散列值, 当执行其他语句时, 其他语句的散列值与当前的散列值进行比较. 而散列值当输入的字母不一样, 它的值都会不同, 这也是为什么我们推荐使用绑定变量, 而不是常量, 因为一旦你使用常量, 那么常量的值不同时, 散列值也就不同, 也就不能利用软解析.
1: select * from employees where department_id = :v_dept;
查看SQL area
1: select sql_text, sql_id, child_number, hash_value, address, executions
2: from v$sql
3: where sql_text like ‘%v_dept‘;
另外, 这里要介绍一下锁存器, 锁存器是 oracle 为了读取存放在 library cache 中信息时获得的一种锁, 锁存器可以保护 library cache 同时被两个会话修改, 或者一个会话正要读取的信息被另一个会话修改而导致的损坏, 在读取 library 中的任何信息之前, oracle 都会获得一个锁存器, 其他会话必须等待, 直到该锁存器被释放它们才能获得锁存器完成工作.
块是 oracle 进行操作的最小单位.
如果我们要读取的数据块在内存中, 那么叫做逻辑读取, 如果我们要读取的块在磁盘中, 我们需要先将其移动带内存中, 再读取, 这叫做物理读取.
我们可以想象, 如果我们的操作都是 软解析+逻辑读取, 那速度肯定是很快, 反过来, 硬解析+物理读取, 就慢很多.
硬解析+物理读取, 软解析+物理读取, 软解析+逻辑读取
1: -- 实验
2: alter system set events ‘immediate trace name flush_cache‘; -- 清空 buffer cache
3: -- alter system flush buffer_cache; -- 10g 新特性, 清空 buffer cache
4: alter system flush shared_pool; -- 清空 share pool
5: set autotrace traceonly statistics -- 只看 sql 语句的统计信息, 也可以 set autotrace on 即看统计信息又看执行计划
6: set autotrace off -- 关闭统计信息
查询转换
在生成执行计划之前, 会有一步查询转换, 该步骤发证在一个查询完成了语法和权限的检查, 优化器为了决定最终的执行计划而为不同的计划计算成本预估之前, 换句话说, 转换和优选是两种不同的任务.
在你的查询通过了语法和权限检查之后, 查询就进入了转换为一系列查询块的转换阶段. 查询块是通过 select 关键字定义的, 如 select * from employees where department_id = 60 这个查询只有一个查询块, select * from employees where department_id in (select department_id from departments) 这个就有两个查询块. 各个查询块要么嵌套在一起, 要么以某种方式相联结. 查询书写的方式决定了查询块之间的关系, 查询转换的主要目的就是确定如果改变查询的写法会不会提供更好的查询计划. 所以, 查询转换能够并且可能会重写你的查询. 你所写的并不一定就是最终确定执行计划的语句. 但是这种转换有可能不是你想要的结果, 尤其是你想要的语句的一定部分执行顺序, 所以需要了解查询转换的内容, 以便写出可以正确得到你想要的行为的SQL语句.
当然查询转换不会改变你的语句的结果集, 例如:
1: -- 查询转换
2: select * from employees where department_id in (select department_id from departments)
3: -- 上面sql, 可能转换为
4: select e.* from employees e, departments d where e.department_id = d.department_id
通过查看执行计划可以了解是否发生了查询转换, 以下几种情况基本会发生查询转换:
- 视图合并
- 子查询解嵌套
- 谓语前推
- 使用物化视图进行查询重写