2012-02-03 169 views
4

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

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; 

改变所有这些情况下看起来更像第一个选项加快了程序极大。我的问题是为什么?为什么在手动组合表格时比选择查询更快?什么是底层流程?

回答

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。

+0

很好的回复。还有比我提供的更好的答案... :-) – 2012-02-03 17:15:40

4

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