优化Oracle存储过程

时间:2012-02-03 13:12:23

标签: sql oracle stored-procedures

我最近负责优化一些现有的Oracle存储过程。每个存储过程查询数据库并生成XML文件输出。特别是大约需要20分钟才能完成执行。看一下它有几个嵌套循环和不必要的查询。例如,而不是做一个

SELECT * from Employee e, Department d WHERE e.DEPT_ID = d.ID
--write data from query to XML

更像是

FOR emp_rec in ( SELECT * from employee )
LOOP
   SELECT * from Department WHERE id = emp_rec.DEPT_ID;
   --write data from query to XML
END LOOP;

将所有这些案例更改为更像第一个选项,大大加快了程序的速度。我的问题是为什么?为什么在选择查询中加入比手动组合表更快?什么是基本流程?

2 个答案:

答案 0 :(得分:5)

让我们来看看原始版本是如何处理的。

FOR emp_rec in ( SELECT * from employee )
LOOP
   SELECT * from Department WHERE id = emp_rec.DEPT_ID;
   --write data from query to XML
END LOOP;

循环查询可能会在employee上执行全表扫描。然后,对于返回的每一行,它将执行内部查询。假设iddepartment的主键,每次执行查询都可能使用主键索引进行唯一查找。

听起来不错,对吧?唯一索引查找通常是获取单行的最快方法(ROWID显式查找除外)。但想想在循环的多次迭代中这是做什么的。据推测,每个员工都属于一个部门;每个部门都有员工;大多数或所有部门都有多名员工。

因此,在循环的多次迭代中,您将多次重复内部查询的完全相同的工作。是的,数据块可能被缓存,因此您没有重复物理读取,但访问缓存中的数据确实会产生一些CPU开销,当反复访问相同的块时,这可能会变得非常重要。

此外,最终您可能希望department中的每一行至少一次,并且可能不止一次。由于表中的每个块都需要被读取,因此您不是通过执行索引查找来保存工作 - 您正在添加工作。

当您将循环重写为单个查询时,优化程序可以将此考虑在内。它有一个选项是执行由employee驱动的嵌套循环连接,这与PL / SQL中的显式循环基本相同(减去Mark指出的上下文切换)。但是,考虑到两个表之间的关系,以及缺少任何过滤谓词,优化器将能够分辨出全部扫描两个表并执行合并或散列连接更有效。这实际上导致更少的物理IO(假设在每次执行开始时干净的缓存)和更少的逻辑IO。

答案 1 :(得分:4)

“基础流程”需要一个很大的答案。我将离开Tom Kyte来回答这个问题;)