巢狀迴圈連線處理的兩個資料集被稱為外部迴圈(outer loop,也就是驅動資料來源,driving row soulce )和內部迴圈〔 inner loop )。外部迴圈為左子節點,內部迴圈為右子節點.如圖10 一6 所示,當外部迴圈執行一次的時候,內部迴圈需要針對外部迴圈返回的每條記錄執行一次。
外部迴圈稱為外表或者驅動表,而內部迴圈稱為內錶或者被驅動表
巢狀迴圈連線有以下幾點特徵:
口左子節點(外部迴圈)只會執行一次,而右子節點(內部迴圈)一般會執行很多次。
口在所有資料處理完之前,就可以返回結果集的第一條記錄。
口可以有效利用索引來處理限制條件與連線條件。
口支援所有型別的連線。
兩表連線
下面是個簡單的兩表巢狀迴圈連線的執行計畫。同時也描述了如何使用提示1eading 與use_n1來強制使用巢狀迴圈連線。leading提示要求先訪問表tl ,也就是,它指定了哪張表作為外部迴圈表使用。use_nl提示指定了具體使用哪種連線方法來將內部迴圈返回的資料(表t2 )與表t1 連線起來。有必要指出的是:usenl 提示並沒有引用表t1 。
nested loops操作是一種相關聯合型操作。它意味著是第二個子節點(內部迴圈)的執行是由第乙個子節點(外部迴圈)控制的。在這個例子中,執行計畫的執行過程可以總結如下。
口 通過全表掃瞄訪問表t1 的所有記錄,訪問中應用限制條件n = 19 過濾資料口
口 前面乙個步驟返回多少條記錄,就在表t2 上執行多少次全表掃瞄。
無疑,當操作2 返回的記錄超過1 條時,這種執行計畫就不是很有效了,因此,查詢優化器幾乎從來不會選擇它。正是因為這個原因,才必須指定兩個訪問提示(full)來強制查詢優化器使用這個執行計畫。另一方面,如果外部迴圈只返回一條記錄,並且內部迴圈的選擇性也很高,對錶t2 進行全表掃瞄可能也不錯。為了展示這一點,下面我們在表t1 的n 欄位卜建立乙個唯一索引:
create unique index t1_n on t1(n)
有了這個索引以後,前面的語句就以下面的執行計一劃執行了。由於操作3 (index unique scan)的緣故,可以確保內部迴圈只會被執行一次。
如果內部迴圈的選擇性很低,對內部迴圈適應索引掃瞄就是乙個較好的選擇。由於巢狀迴圈連線是相關聯合型操作,對內部迴圈來講,這個地方也可能利用到連線條件。例如,在下面的執行計畫中,操作5就是利用操作3的返回值t1.id做了索引查詢。
select /* + leading (t1) use_nl(t2) index(t1) index(t2)*/ *
from t1 ,t2 where tl.id =t2.id and tl.n=19
這樣,通過建立2個索引,使得效能得到最好的優化。巢狀迴圈適合2個選擇性低的效率資料量的選擇,到資料量大的時候優化器會預設選擇hash join 連線,除非我們手動選擇提示,並且通過索引來達到很好的效率。
總的來講,如果內部迴圈會執行多次,只有具有好的選擇性的訪問路徑以及導致比較少的邏輯讀的路徑才有意義。
四表連線
下面的執行計畫是個典型的用巢狀迴圈連線實現的左深樹。請注意各個表是如何通過索引進行訪問的ordered 提示要求這些表按它們在from 子句中的順序進行訪問。use_nl提示要求後面的表與第一張表(或者前面乙個操作的結果集)進行連線的時候使用巢狀迴圈連線。
select /* + ordered use_nl(t2 t3 t4)*/ t1.*,t2.*,t3.*,t4.*
from t1,t2,t3,t4
where t1.id = t2.t1_id
and t2.id =t3.t2_id
and t3.id =t4.t3_id
and t1.n = 19
這種執行計畫的執行流程可以總結如下〔 下面的描述不考慮使用行預取的情況):
( l )當讀取第一條記錄的時候(也就是說,不是當這條語句解析或者執行的時候),在應用了表t1 上的限制條件tl.n = 19 並取得第一條記錄的時候,處理過程就開始了。
( 2 )表t2 基於表t1 找到的結果進行查詢。資料庫引擎會利用連線條件tl.id = t2.t1_id 來訪問表t2。事實上.表t2 上沒有任何限制條件。只有第一條滿足連線條件的記錄才會返回給上級操作。
( 3 )表t3 基於表t2 找到的結果進行查詢資料庫引擎也是利用連線條件t2.id =t3.t2_id 來訪問表t3 。只有第一條滿足連線條件的記錄才會返回給上一級操作。
( 4 )表t4 基於表t3 找到的結果進行查詢。這兒也一樣,資料庫引擎也是利用連線條件t3.id =t4.t3_id 來訪問表t4 。滿足條件的第一條記錄會立即返回給客戶端。
( 5 )後續的操作也是按照第一條記錄樣的行為來進行的。很明顯,執行過程是在卜一次匹配(也可能是表t4 裡面匹配的第二條記錄,如果有的話)的位置開始的。需要特別強調的是,滿足條件的記錄會在第一時間被返回給客戶端。換句話講,就是完全沒有必要在返回第條記錄之前完成整個執行過程。
塊預取
在一般情況下,當快取沒有命中的時候,基於單塊處理(例如,rowid 訪問、索引範圍掃瞄)的訪問路徑會導致乙個單塊的物理讀。對於巢狀迴圈連線來講,特別是當有很多行資料需要處理的時候,效率會比較差。實際卜,很多時候,巢狀迴圈連線也會使用多個單塊物理讀來訪問多個相鄰的塊。資料庫引擎可以利用塊預取功能來提高巢狀迴圈連線的效率。這種優化技巧的目的是,對與多個相鄰的塊使用一次多塊物理讀取,來代替多次單塊物理讀取。塊預取對於表和索引都是適用的。無法通過檢視執行計畫來看出資料庫引擎是否使用了塊預取功能。唯一能夠看到的方式是,檢視伺服器程序執行的物理讀,特別是與物理讀相關的等待事件。
口db file sequential read 是個與單塊物理讀取相關的事件。因此,如果這個事件出現,就意味著塊預取要麼沒有被使用,要麼是無法被使用(例如,由於請求的塊己經在快取記憶體中)。
口db file scattered read 是乙個與多塊物理讀取相關的事件。因此.如果在~id 訪問或者索引範圍掃瞄中看到這個等待事件,就表明在使用塊預取功能。
需要特別提示的是,我們無法控制塊預取功能的使用。如何以及是否使用塊預取功能是由資料庫引擎決定的。
其他可選的執行計畫
可以用下面的執行計畫來執行巢狀迴圈連線。
實際上,在oracle 的最近幾個版本中,只有當內部迴圈或者外部迴圈是基於唯一索引掃瞄(index unique scan )的時候才會使用這種型別的執行計畫。下面我們來看看,如果在列n 上的索引t1_n 如下面這樣定義(為非唯一)時會如何:
create index t1_n on t1(n)
當換成這個索引時,下面的執行計畫將被採用。請注意,表t2 上的rowid 訪問是處在不同的位置的.在前乙個執行計畫,它是在操作4 ,然而在後乙個執行計畫中,它是在操作1 。比較特別的是,row 記訪問(操作l )的子操作是乙個巢狀迴圈連線(操作2 )。從我們的角度看,這兩個執行計畫做了同一件事。卜面的這個執行計畫可能是為了利用一些內部優化(比如塊預取)。
在oracle 11g 中,可能會使用下面的執行計畫,而不是之前的那個。注意,雖然查詢始終是乙個兩表連線,執行計畫卻含有兩個巢狀迴圈連線!乙個簡單的效能測試顯示,使用它可以將效能提高約10 個百分點。這可能是由於一種新的內部優化只能在新的執行計畫中才能生效。
oracle巢狀迴圈的執行計畫優化
巢狀迴圈連線處理的兩個資料集被稱為外部迴圈 outer loop,也就是驅動資料來源,driving row soulce 和內部迴圈 inner loop 外部迴圈為左子節點,內部迴圈為右子節點 如圖10 一6 所示,當外部迴圈執行一次的時候,內部迴圈需要針對外部迴圈返回的每條記錄執行一次。巢狀迴...
for迴圈巢狀執行效率
今天做專案時遇到乙個for迴圈的巢狀問題,乙個迴圈次數多,乙個次數少,怎樣設計效率較高.想起以前筆試時遇到過這個問題,當時由於時間倉促,沒有細想,今天在實際 中遇到這麼問題,於是動筆算了下.設兩個迴圈次數分別為m n,m為乙個很大的數,n為乙個較小的數。假設在m迴圈中第i 0則大迴圈套小迴圈所需的運...
ocacle 執行計畫 Oracle執行計畫
一 什麼是oracle執行計畫?執行計畫是一條查詢語句在oracle中的執行過程或訪問路徑的描述 二 怎樣檢視oracle執行計畫?因為我一直用的plsql遠端連線的公司資料庫,所以這裡以plsql為例 配置執行計畫需要顯示的項 工具 首選項 視窗型別 計畫視窗 根據需要配置要顯示在執行計畫中的列 ...