2012-02-03 168 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回答這個問題;)