天天看點

《Oracle高性能SQL引擎剖析:SQL優化與調優機制詳解》一2.4 執行計劃各個操作的含義

通常我們所說的執行計劃操作包含兩個部分:操作與其選項。例如,哈希關聯反關聯(hash join anti)中,哈希關聯(hash join)是一種操作,“反”關聯(anti)則是其選項;該操作還可以與其他選項(如“半”關聯,semi)配合形成不同的執行計劃操作。

執行計劃中的操作數量非常多。我們下面列出的操作是oracle 10gr2中的絕大多數操作。oracle的每個版本都會有一些新的特性出現,而其中一些新特性又會帶來新的操作,或者抛棄一些舊操作。如果發現執行計劃出現新操作,讀者可以結合相關新特性的描述來了解該操作的含義。

實際上,在執行計劃裡出現的操作包含兩個資訊,一個是操作類型(在plan_table、v$sql_plan等表或視圖中,字段名為operation);一個是操作的選項(在相關表和視圖中,字段名為options)。例如table access by index rowid,它的操作類型是table access,即通路表,選項是by index rowid,即通過索引中的rowid來通路表。

提示:在11g中,可以通過固化表x$xplton和x$xpltoo分别查詢出所有操作類型和選項名稱。一個操作可以有0到多個選項。但并不是所有選項都能用于所有的操作。

為了便于讀者更好地了解這些操作,我們對這些操作類型進行了歸類。但這個歸類并非是一個絕對的劃分,例如,某些操作可以劃分到多個類别中(如merge join partition outer,既是一個資料關聯操作,又是一個分區操作),但我們會按照操作的相關性将其劃分在某個類别中。

這些操作代表了這條語句的類型,在執行計劃中,它們出現在id為0的操作中。我們還可以将該類型操作分為資料定義語句(ddl)類型和資料管理語句(dml)類型,如表2-2所示。

《Oracle高性能SQL引擎剖析:SQL優化與調優機制詳解》一2.4 執行計劃各個操作的含義

create table statement執行建立表的語句,是最為常用的,其示例如下:

merge語句執行合并操作是比較難的,其示例如下:

對資料庫對象的通路路徑(access path)方法有很多,根據通路對象不同,可以将這類操作分為表通路操作、索引通路操作、固态表通路操作和物化視圖通路操作。下面分别介紹這些操作,括号中内容為操作選項。

2.4.2.1 表通路操作

(1)table access (full)

全表掃描,通過完全掃描的方式通路表。

(2)load as select

以追加(append)模式向表中插入資料。示例如下:

(3)table access (by index rowid)

通過由索引中擷取到的rowid通路表。

(4)table access by local index rowid

通過由本地分區索引中擷取到的rowid通路表。

關鍵詞釋義

分區表:我們可以将表中的資料按照特定規則分開存儲在不同的位置,這樣的表即為分區表。每個分區都作為一個單獨的段進行管理。在分區表上建立索引時,可以指定索引是否也按照相同規則分開存儲。如果索引也分開存儲,就是本地分區索引,否則為全局分區索引。

示例如下:

(5)table access (by global index rowid)

通過由全局分區索引中擷取到的rowid通路表。注意,全局分區索引并不一定建立在分區表上,也可以建立在非分區表上。

(6)table access (by user rowid)

通過使用者輸入或者從子查詢中擷取到的rowid通路表。

(7)table access (by rowid range)

通過一段範圍的多個rowid來通路表。示例如下:

提示:我們可以通過sql“提示”來強制語句執行計劃中進行特定的操作。

(8)table access (cluster)

通過簇來通路表。

簇(cluster):如果多個表可以共享一個或多個字段的資料,并且需要經常通過這些字段關聯通路這些表時,我們可以将這些字段建立為一個簇,而這些表則可以圍繞該簇建立為簇表。簇是一個單獨的實體對象。

2 - access("a"=to_number(:a))

(9)table access (hash)

通過哈希簇來通路表。

提示:簇的存儲結構有兩種。一種是索引簇,即按照普通b*數的結構存儲;另一種是哈希簇,即按照資料的哈希值進行存儲。

(10)table access (sample)

采樣通路表,即以多資料塊讀取的方式掃描表的部分資料塊。

某些時候,我們可能并不需要得到一個精确的結果,而是通過采樣的方式通路表中的資料,然後按采樣比例計算出大概結果。此時,我們就可以通過采樣的方式通路表。

(11)table access (sample by rowid range)

通過對指定的一段範圍的rowid,以采樣的方式通路表。示例如下:

hellodba.com>exec sql_explain('select * from t_xpl sample(5) where rowid>:a', 'typical');

2.4.2.2 索引通路操作

(1)index (unique scan)

唯一索引掃描,即對唯一索引進行單一比對通路。在唯一索引中,每一個非空鍵值隻會存在一條。主鍵本身也是一個唯一索引。示例如下:

(2)index (range scan)

索引範圍掃描,即對(唯一或非唯一)索引進行範圍比對(>、<、>=、<=、like)通路,或者對非唯一索引進行單一比對通路。

(3)index (range scan (min/max))

對索引進行範圍掃描,以擷取索引字段的最大值、最小值。

(4)index (range scan descending)

按照與索引邏輯順序的相反順序對索引進行範圍掃描。

(5)index (fast full scan)

快速完全索引掃描,對索引進行快速完全掃描通路。這種通路方式中,不會按照索引的邏輯順序通路,而是按照實體順序讀取所有的索引資料塊,并且能夠每次讀取多個資料塊。

提示:在b*tree索引中,含有空鍵值的資料記錄不會被建構到索引中。是以,如果索引字段允許為空的話,在查詢索引字段資料時,如果未限制擷取非空資料,則無法進行索引快速完全掃描。

(6)index (sample fast full scan)

索引快速完全采樣掃描。與采樣通路表類似,即以多資料塊讀取的方式掃描索引的部分資料塊。示例如下:

(7)index (full scan)

全索引掃描,即對索引進行完全掃描通路。這種通路方式與索引快速完全掃描的差別在于:

1)它是按照索引資料的邏輯順序而不是實體存儲順序讀取;

2)每次隻能讀取一個而不是多個資料塊。

(8)index (full scan (min/max))

對索引進行完全掃描通路,以擷取索引字段的最大值、最小值。

(9)index (full scan descending)

以索引邏輯順序相反的順序對索引進行完全掃描通路。

(10)index (skip scan)

跳躍索引掃描,即對多字段複合索引掃描時,跳過索引中前導的一個或多個字段,而對後續字段進行比對。

提示:在建立多字段複合索引時,oracle會先按字段的順序建構索引樹,如果新資料記錄的第一個字段值與索引中已有資料記錄的第一個字段值相同,則會對第二個字段進行比對來排序,以此類推。如果索引中的前導字段(可以是一個或者多個)的可區分數值非常少,即大多數資料記錄擁有相同的數值,那麼建構的索引樹的邏輯結構,大多數都是由後續字段的順序決定。而在這種情況下,如果一個查詢條件中隻含有後續字段的過濾條件,在對索引進行掃描時,可以将索引樹視為多個小的索引樹進行掃描,這種掃描方式就是跳躍索引掃描。

(11)index (skip scan descending)

以複合索引前導字段邏輯順序的相反順序進行跳躍索引掃描。

(12)domain index

通路域索引。

域索引:在oracle中,除了兩種内建的資料結構(b*tree和位圖)外,使用者還可以對索引結構進行擴充,建立其他結構的索引,例如四叉樹,這需要建立、維護和通路索引的函數支援。由于這樣的索引通常用于某個應用領域内,如全文檢索。是以,這種索引稱為域索引。

2 - access("ctxsys"."contains"("table_name",'t')>0)

(13)bitmap index (single value)

位圖索引單值範圍,即對位圖索引中的一個鍵值進行比對通路。

位圖索引:在oracle中,存在兩種資料結構的索引,一種是普通索引,以b*樹結構建構索引,整個索引是樹狀結構,在葉子節點(即最底層節點)上存儲索引記錄,每條索引記錄中包含了索引字段值和對應資料記錄的rowid,表中的每條資料記錄(索引字段非空)在索引中都有一條索引記錄,如果索引字段數值相同,則按rowid順序存儲;另一種是位圖索引,位圖索引也是樹狀結構,但是它并不是對表中的每條資料記錄都建構一條索引記錄,而是将多個實體位置連續的資料記錄映射到一條索引記錄中,在葉子節點中,索引記錄中除了索引字段值外,還包括其映射的多條表資料記錄的起始位址和結束位址,最重要的是,它還包含一個位圖,位圖的每一個(bit)按序對應了一條表記錄,位的值為1,說明表記錄中的索引字段數值與索引記錄中的數值相同,為0則表示表記錄與索引記錄的索引字段值不比對。我們會在後面章節詳細介紹位圖索引的存儲方式。

要注意的是,位圖索引包含了索引字段為空的資料記錄。

在oracle(9i及以上版本)中,還有一種特殊的位圖索引:位圖關聯索引(bitmap join index)。建立這樣的位圖索引時,可以與其他表的相關字段進行關聯。在對它們以索引字段作為過濾條件進行關聯查詢時,就可以避免對關聯表的讀取。建立了位圖關聯索引後,oracle會在建立索引的表上建立一個隐藏的虛拟字段,用于優化器選擇和産生對應的通路路徑。

(14)bitmap index (range scan)

位圖索引範圍掃描,即對位圖索引中的多個鍵值進行比對通路。

(15)bitmap index (full scan)

位圖索引完全掃描,掃描方式與普通索引完全掃描類似。

(16)bitmap index (fast full scan)

位圖索引快速完全掃描,掃描方式與普通索引快速完全掃描類似。

2.4.2.3 固态表通路操作

(1)fixed table (full)

固态表完全掃描。

固态表是oracle中的一種特殊表,以x$開頭,屬于sys使用者。它們存儲的是oracle執行個體内部使用的運作資料,如一些性能統計資料、latch資訊等;在執行個體啟動時加載到記憶體中,并在資料庫的運作過程中動态添加删除,在執行個體關閉時被釋放。系統中所有固态表可以由視圖v$fixed_table查詢得到。除sys使用者外,其他使用者都不能直接查詢固态表,隻能通過視圖查詢。

(2)fixed table (fixed index)

通路固态表上的固态索引。示例如下:

(3)fast dual

快速通路dual表。

dual表是oracle中的一個特殊表,它隻有一個字段、一條記錄。有時候,我們需要通過sql來擷取一些特殊資料,如目前系統時間、一個表達式的結果等,可以通過select from dual的方式擷取。而實際上,我們不關心dual表本身的資料,隻是通過它來構造出一條完整的語句。在fast dual出現(10g)之前,oracle需要實際讀取一次dual表以完成一個語句的生命周期。引入fast dual以後,則不需要去真正通路dual表,而是通過虛拟通路直接傳回給上一層調用者。

注意,如果select後的表達式中含有dual表的字段(dummy,或者*)時,還是會實際讀取dual表的。

2.4.2.4 物化視圖通路操作

(1)mat_view access (full)

物化視圖(materialized view)完全掃描。

物化視圖是一個實體對象,其資料存儲在相應的存儲段(segment)上。

(2)mat_view access (by index rowid)

通過由索引中擷取到的rowid通路物化視圖。

(3)mat_view access (by user rowid)

通過使用者輸入或者從子查詢中擷取到的rowid通路物化視圖。

(4)mat_view access (by rowid range)

通過一段範圍的多個rowid來通路物化視圖

(5)mat_view access (sample)

采樣通路物化視圖,即以多資料塊讀取的方式掃描物化視圖的部分資料塊。

(6)mat_view access (sample by rowid range)

通過對指定的一段範圍的rowid,以采樣的方式通路物化視圖。

(7)mat_view access (rewrite access (full))

将查詢重寫後,完全掃描物化視圖。示例如代碼清單2-2所示。

提示:啟用物化視圖查詢重寫特性(物化視圖本身要啟用重寫特性,并且系統或目前會話參數query_rewrite_enabled為true)後,如果通過物化視圖通路能確定資料的完整性(檢查規則由參數query_rewrite_integrity決定),則優化器會考慮查詢重寫,并通路相應物化視圖。

《Oracle高性能SQL引擎剖析:SQL優化與調優機制詳解》一2.4 執行計劃各個操作的含義

(8)mat_view access (rewrite access (by index rowid))

在查詢重寫後,通過索引通路物化視圖。

該類操作基于位圖資料(例如位圖索引的索引記錄)進行位操作,或者将其他資料結構與位圖資料互換以利用位操作。

(1)bitmap and

對位圖進行“與”(and)操作。

位圖索引中位圖的每個位代表了其所對應的表記錄中索引字段的值是否為索引記錄中的值。如果有兩個位圖索引需要進行數值比對過濾,則隻需将表記錄對應的位進行與操作就可以知道該記錄是否滿足條件。

(2)bitmap or

對位圖進行“或”(or)操作。在查詢的過濾條件中,如果位圖索引字段直接的關系是“或”,可以通過bitmap or來判斷位圖所映射的一批資料記錄是否滿足條件。

(3)bitmap conversion from rowids

将一批資料記錄的rowid映射為位圖。

對于普通b*樹索引,oracle也可以将資料記錄的rowid映射成一個位圖,然後進行位圖操作。進行這樣的轉換需要将系統參數_b_tree_bitmap_plans設定為true。

(4)bitmap conversion to rowids

将位圖映射為rowid。在一個位圖鍵值中,包含了一批資料記錄的起始位址和結束位址,且這批記錄是連續的,是以位圖中的每一個位就按序對應了一條資料記錄。示例如下:

(5)bitmap conversion count

對位圖進行計數操作。

(6)bitmap merge

将多個位圖合并成一個位圖。

(7)bitmap minus

對兩個位圖進行集合相減的操作。

一個位圖映射了一批資料記錄,在對多個位圖索引中的一個或多個位圖做排除過濾時,可以直接進行位圖的相減操作。示例如下:

(8)bitmap key iteration

對位圖索引鍵值進行疊代。

在關系資料庫中,可能會存在一些這樣的資料:某個表存儲大量的實際資料,而其中有多個字段和不同的表存在關聯關系。在查詢時,這張表需要與多個表進行關聯,以擷取關聯資料或由關聯表對資料進行過濾,這樣的查詢稱為星形查詢,存儲實際基礎資料表又稱為事實表,關聯表則稱為次元表。例如,一個系統中的使用者資料表往往是一個龐大的表,而使用者有許多屬性,如省份、所屬機關、畢業學校等,會與其他多個表,如省份表、機關表、學校表等,發生關聯。在這樣一個環境中,事實表資料量相當龐大,而次元表的資料都比較少。按照星形查詢的邏輯關系,即以事實表為中心,與多個次元關聯資料進行過濾,在星形轉換(star transformantion)的優化方式出現之前,需要以事實表為驅動表,再與多個次元進行關聯查詢,擷取到資料之後,再對資料進行過濾,如果過濾的資料較多,這樣查詢過程的額外代價相當大。而在oracle7i之後,如果事實表的這些關聯字段上建立了位圖索引後(或者啟用了b*樹位圖轉換特性後),優化器可以對這樣的查詢進行星形轉換:即先由次元擷取到過濾後的資料,由得到的關聯字段中的數值對事實表的位圖索引字段進行疊代,再将疊代到的位圖進行合并,最後将位圖轉換為rowid以擷取事實表的資料。這樣,就可以最大限度地減少讀取事實表資料再進行過濾的額外開銷。示例如代碼清單2-3所示。

提示:星形轉換隻能在cbo模式下生效,且語句不能有綁定變量,優化器參數star_transformation_enabled必須為true。

以下操作與資料排序相關。

(1)buffer sort

在記憶體中進行排序操作。有兩點需要說明:

1)buffer sort并不代表一定會進行排序操作。有時是oracle為了借助私有記憶體的工作區來完成其他操作。

2)這裡的記憶體不是共享記憶體(buffer cache),而是會話程序的私有記憶體(pga)。

(2)sort aggregate

通過對資料進行排序,以擷取一個聚集結果。

注意,排序聚集操作并不一定意味着存在“排序”過程,例如count()函數計數。并且,它也不要求在私有記憶體的排序工作區中完成。示例如下:

(3)sort (create index)

通過對資料進行排序,以建構一個索引。

(4)sort (group by)

通過對資料進行排序,以進行分組操作。

(5)sort (group by rollup)

通過對資料進行排序,以進行分組和合計操作。

(6)sort (group by stopkey)

通過對資料進行排序,以進行分組操作,并且在達到終止條件(僞列rownum不滿足條件)時終止操作。

rownum是一個僞列,即它的資料并非實際資料,而是在擷取資料(fetch)時産生的。它的數值代表了資料結果集中每一行資料的序号。

當rownum和其他條件共同過濾查詢時,擷取到滿足所有條件的限定數量的資料後,查詢就結束了,不再讀取和判斷剩餘資料。

(7)sort (group by nosort)

無需排序(資料實體順序與邏輯順序一緻),對資料進行分組,因而無需再進行排序操作。

通過索引完全掃描方式讀取到的資料就是已經排序好的資料。

(8)sort (group by nosort rollup)

無需排序(資料實體順序與邏輯順序一緻),對資料進行分組和合計,因而無需再進行排序操作。示例如下:

(9)sort (order by)

将資料排序,以實作資料的按序輸出。

(10)sort (order by stopkey)

将資料排序,以實作資料的按序輸出,并且當滿足排序個數(rownum)時,停止排序。

按照某些排序方法(如選擇排序)對一組資料進行排序時,會在每一輪中找到最小(或最大)資料;在對查詢結果限制輸出數量時,我們隻需要找到n個最小(或最大)資料,無需再對剩餘資料排序。

(11)sort (unique)

對資料進行唯一排序,丢棄重複資料。

(12)sort (unique nosort)

從已排序的資料中擷取唯一資料,丢棄重複資料。示例如下:

(13)sort (unique stopkey)

對資料進行唯一排序,丢棄重複資料,并且當滿足排序個數(rownum)時,停止排序。

(14)sort partition join

這一操作出現在分區外關聯過程中。它對資料進行排序,進而對資料進行分組(分區)。參見下節中的示例。

以下操作為資料集關聯(join)的操作。

(1)nested loops

通過嵌套循環擷取關聯資料。在進行嵌套循環關聯時,第一個資料集是驅動資料集,即嵌套循環中的外循環。

(2)nested loops (anti)

通過嵌套循環擷取非關聯資料。

在nested loops (anti)的過程中,如果發現外循環中讀取到的資料在内循環中能夠比對到,則立即終止内循環、丢棄該資料,開始下一輪循環。

(3)nested loops (semi)

通過嵌套循環擷取不完整關聯資料。

在nested loops (semi)的過程中,如果發現外循環中讀取到的資料在内循環中能夠比對到,則立即終止内循環、傳回該資料,開始下一輪循環。

(4)nested loops (outer)

通過嵌套循環進行外關聯,擷取關聯資料。

我們通常說的關聯,如果沒有特别指明,都是說内關聯。而外關聯與内關聯的不同之處在于,無論在内循環中是否找到比對資料,外循環中的資料都會被傳回。

(5)nested loops (partition outer)

以左外關聯的左邊資料集(或右外關聯的右邊資料集)為外循環,将左外關聯的右邊資料集(或右外關聯的左邊資料集)分組(分區)進行外關聯比對。

提示:對于分區左(右)外關聯,從邏輯上看,左(右)表需要與右(左)表中的資料分組(分區)分别做外關聯。如果實際操作也按照這個邏輯實作,則意味着每次與一組資料進行關聯,都要讀取一次左(右)表資料。而在nested loops partition outer中,第一次讀取左(右)表資料後,就被緩存在私有記憶體中,進而避免了多次重複讀取共享記憶體資料。

(6)hash join

通過資料集的哈希值比對擷取關聯資料。

進行哈希關聯時,先用哈希函數對左資料集(也稱為建構資料集)按哈希值分區,并且建立哈希表,映射哈希鍵值與分區;然後逐條掃描右資料集(也稱為探測資料集)的資料記錄,用相同的哈希函數擷取哈希值,并與哈希表進行比對,找到相應的建構資料集的哈希分區,然後再用分區中的資料進行精确比對。

哈希值與原始資料之間的關系是一對多的關系。即對于同一個雜湊演算法,一條原始資料隻會有一個哈希值,且多條不同資料的哈希值可能相同。

(7)hash join (anti)

通過資料集的哈希值比對擷取非關聯資料。

在比對的過程中,如果哈希值相同、且數值比對,則立即終止對關聯哈希表中剩餘哈希值的比對、丢棄該哈希值,開始下一輪比對。

提示:如果作為驅動的資料集的哈希表非常大,以至于記憶體中哈希空間無法一次性完成兩邊哈希表的比對,則會将驅動哈希表分為一個小的哈希表,一次比對一個,其他的則暫時存儲到臨時表空間中。我們可以注意到上述執行計劃中有對臨時表空間的估算值(tempspc)。

(8)hash join (right anti)

取右邊資料集做驅動,通過資料集的哈希值比對擷取非關聯資料。

如果驅動資料集太大,以至于需要配置設定臨時空間暫存哈希表,優化器則會考慮采用資料量較少的關聯資料集作為驅動。此時,就需要用hash join(right anti)進行關聯;同樣,在比對的過程中,如果兩邊的哈希值相同且數值比對,則立即終止對關聯哈希表中剩餘哈希值的比對、丢棄條數,開始下一輪比對;不同的是,如果所有記錄都未比對,則傳回關聯資料集中的資料,而非驅動資料集的資料。

(9)hash join (anti sna)

通過資料集的哈希值比對擷取非關聯資料,并且同時關注是否有空值。這一操作在11g中引入。非關聯查詢時,是否進行檢測控制,可以由優化器參數“_optimizer_null_aware_antijoin”控制。

示例(oracle版本11.2.0.1)如下:

(10)hash join (right anti sna)

取右邊資料集做驅動,通過資料集的哈希值比對擷取非關聯資料,同時關注是否有空值。這一操作在11g中引入。

(11)hash join (semi)

通過資料集的哈希值比對擷取不完整關聯資料。

在比對過程中,如果哈希值相同、且數值比對,則立即終止對關聯哈希表中剩餘哈希值的比對、傳回該哈希值,開始下一輪比對。

(12)hash join (right semi)

取右邊資料集做驅動,通過資料集的哈希值比對擷取不完整關聯資料。

如果驅動資料集太大,為了避免讀寫臨時表空間,優化器會考慮通過hash join (right semi)進行關聯;同樣,在比對過程中,如果哈希值相同且數值比對,則立即終止對關聯哈希表中剩餘哈希值的比對、傳回關聯資料集中的哈希值,開始下一輪比對;傳回關聯資料集中的資料,而非驅動資料集的資料。

(13)hash join (outer)

通過資料集的哈希值比對進行外(左)關聯資料關聯。

這裡的外關聯,實際上就是左外關聯。通過哈希值比對進行外關聯操作時,左資料集的資料無論是否比對到右資料集,都會被擷取。

(14)hash join (right outer)

通過資料集的哈希值比對進行右外關聯資料關聯。

(15)hash join (full outer)

通過資料集的哈希值比對進行關聯資料完全關聯。

提示:該操作在10.2.0.4以後引入。

示例(oracle版本10.2.0.4)如下:

(16)merge join

通過合并已排序的資料進行關聯。示例參見下例。

進行合并關聯時,分别從兩個資料集的首位開始對資料進行比較,如果相等,則說明資料比對,則對兩邊資料集的下一條資料進行比較;如果不相等,則擷取較小數值(如果為降序,則是大數值)所在資料集的下一條記錄;當兩邊資料集都存在多條相等的記錄時,需要在這多條記錄之間進行多重投影比對(multiple cast)。

(17)sort (join)

對資料進行排序,以執行合并關聯(merge join)操作。

排序後的資料緩存在私有記憶體中,因而可以減少對共享緩存的通路次數和鎖閥(latch)的争用。

提示:在做合并關聯時,要求兩邊資料集已經排好序。

(18)merge join (anti)

通過進行資料合并關聯擷取非關聯資料。

通過合并關聯擷取非關聯資料時,如果左指針指向的資料大于右邊資料,則右指針向後移一位,進行下一次比較;如果相等,兩邊指針都向後移一位;如果左邊資料小于右邊資料,則傳回左資料(因為右邊不會存在與其相等的資料了),并且指針向後移動。

由于右資料集隻是起到資料比較的作用,而不需要傳回資料,是以會對右資料集進行唯一性排序,排除重複資料,以減少比對次數。

(19)merge join (semi)

通過進行資料合并關聯擷取不完整關聯資料。

merge join(semi)與merge join不同之處在于,由于右資料集不需要傳回資料,且與左資料集的資料記錄相等的資料隻需要比對一次,是以右資料集會進行唯一性排序,以排除重複資料。

(20)merge join (outer)

通過進行資料合并關聯而進行(左)外關聯,擷取關聯資料。

通過合并關聯進行左外關聯時,當右邊資料記錄大于、等于左邊資料記錄時,都屬于比對記錄。

(21)merge join (partition outer)

将右邊資料集分組(分區),左資料集分别與每組資料進行合并外關聯,以擷取關聯資料。

(22)merge join (cartesian)

通過合并關聯對資料集進行笛卡兒關聯。

進行笛卡兒關聯時,兩邊資料集中的任何一條資料記錄都會與關聯資料集中的任何一條記錄比對,換句話說,它們之間不存在關聯條件,兩邊資料集的任何兩條記錄都滿足笛卡兒的關聯比對要求。

(23)join filter (create)

建立一個過濾器,以用于布隆過濾器(bloom filter),示例參見下例。

(24)join filter (use)

使用系統建立的過濾器進行布隆過濾。

布隆過濾器(bloom filter)是用于判斷一個元素是否屬于一個資料集的資料結構。其基本思想就是用一個或多個哈希函數對資料集中的每個成員做映射,映射結果不是存在完整的哈希表中,而是一個位向量(bit vector)中。位向量所有位初始都為0,根據哈希結果将位向量中的相應位置1。對資料集中的所有成員的哈希計算完成後,就得到了該資料集的位向量。當需要判斷一個元素是否屬于該資料集時,也用相同的哈希函數對其映射得到它的位向量,然後将其位向量上所有為1的位與資料集位向量上相應位比較,如果發現資料集的位向量上某個位為0,可以判斷這個元素不屬于該資料集。而如果所有相應位都為1的話,那麼該元素可能屬于這個資料集。

下面介紹兩個示例,如代碼清單2-4和代碼清單2-5 所示。

代碼清單2-4 利用哈希關聯的雜湊演算法建立布隆過濾器(需在11gr2中運作)

(25)part join filter (create)

在哈希關聯時建立布隆過濾,用于分區裁剪。該操作在11g被引入。

(26)partition hash (join-filter)

利用布隆過濾,進行分區裁剪。示例如代碼清單2-6所示。

代碼清單2-6 part join filter (create)和partition hash (join-filter)示例(oracle版本11.2.0.1)

對具有層次關系的資料記錄可用以下查詢操作。

(1)connect by (with filtering)

在查詢層次關系資料時,對父子關系資料進行連接配接。并且在連接配接資料時,對資料進行過濾。

這裡的父子關系不是指關系表之間的聯系,而是指資料行之間的層次關系,這個關系由一個或多個字段指明。

(2)connect by with (filtering(unique))

在查詢樹狀關系資料時,對父子關系資料進行連接配接。并且在連接配接資料時,對資料進行過濾,并獲得不重複資料。示例如下:

(3)connect by (without filtering)

在查詢層次關系資料時,對父子關系資料進行連接配接。在連接配接資料時,不對資料進行過濾。

(4)connect by without filtering(unique)

在查詢樹狀關系資料時,對父子關系資料進行連接配接。在連接配接資料時,不對資料進行過濾,并獲得不重複資料。示例如下:

(5)connect by (no filtering with start-with)

在查詢樹狀關系資料時,對父子關系資料進行連接配接。并結合start with的條件連接配接資料。

(6)connect by (no filtering with sw (unique))

在查詢樹狀關系資料時,對父子關系資料進行連接配接。結合start with的條件連接配接資料,并獲得不重複資料。示例如下:

(7)connect by pump

在查詢層次關系資料時,由對資料泵讀取的資料進行連接配接,建立資料的層次關系。

這個層次關系的建立是通過表的自我關聯(self join)實作的。當樹狀查詢存在過濾條件(start with)時,需要先通路表擷取到過濾後的資料集,然後再周遊該資料集、進行自我關聯建立層次關系。在周遊資料集時,為了避免再次通路表,oracle建立了一個通道(即由資料泵連接配接)直接通路已經讀取的資料。示例如代碼清單2-7所示。

提示:是否啟用該特性,可以通過參數_old_connect_by_enabled控制,false為啟用,true為禁用。

與視圖、子查詢相關的操作如下所示。

(1)view

查詢未與主查詢合并的視圖。

優化器在生産執行計劃的過程中,查詢轉換器會嘗試将子查詢或視圖的查詢語句與主查詢進行合并重寫。但在某些情況下,如通過提示禁止合并、需要擷取子查詢的僞列rownum資料等,則不會合并子查詢或視圖。

(2)view pushed predicate

查詢未與主查詢合并的視圖,并将主查詢中比對條件推入視圖查詢語句中。示例如代碼清單2-8所示。

代碼清單2-8 view pushed predicate示例

下面是針對集合進行的操作。

(1)concatenation

将兩個或多個結果集進行拼接。

拼接實際上就是對多個集合的簡單相加。為了消除被重複擷取的資料,前一個集合的過濾條件會累加在後一個集合上。例如,在下例當中,第二個集合的操作上加上了過濾條件“lnnvl("table_name"=:c)”;第三個集合的操作上加上了過濾條件“lnnvl("table_name"=:c) and lnnvl("owner"=:a)”。示例如代碼清單2-9所示。

提示:oracle中,表達式存在null數值,則表達式結果為null,是以進行資料比對時,null資料僅在“is null”表達式中為true。而函數lnnvl確定null資料也被比對。它的參數為一個表達式,當表達式結果為fasle或者null時傳回true,否則傳回false。

代碼清單2-9 concatenation示例

(2)union-all

将兩個或多個資料集進行聯合(或者說資料集相加)。

union和union-all的不同之處在于,union會消除聯合後的資料集中的重複資料,union-all則不會;union與concatenation的不同之處在于,union是在資料集相加之後,利用其他操作消除重複值,而concatenation則是在擷取資料集時增加過濾條件消除重複值。

(3)union-all (partition)

對分區視圖中的分區結果集進行聯合。

提示:分區視圖是在oracle 7i中引入的技術,将大表拆分成小表,建立視圖,以擷取更好的性能。在8i中,oracle引入了分區表技術,在許多方面(如性能、可管理性)上超越了分區視圖。

(4)union all pushed predicate

将查詢謂詞推入聯合查詢的子查詢或視圖中。示例如代碼清單2-10所示。

提示:如果視圖或子查詢為“union all”,要實作該操作則需要確定優化器參數“_push_join_union_view”為true(預設);如果視圖或子查詢為“union”,要實作該操作則需要確定優化器參數“_push_join_union_view2”為true(預設)。

代碼清單2-10 union all pushed predicate示例

(5)intersection

取兩個資料集的交集。

注意,交集中的資料不存在重複資料。

(6)minus

兩個資料集相減。

注意,相減後的資料不存在重複資料。a資料集減去b資料集,相當于a資料集中去除了兩個資料集的交集的資料以及重複資料。

(7)and-equal

對由兩個或多個單字段索引擷取rowid資料集的交集,并消除重複的rowid。示例如代碼清單2-11所示。

代碼清單2-11 and-equal 示例

該分類中包含了所有與分區(partition)相關的操作。注意,還有一些與分區相關的操作(例如,nested loops (partition outer))被歸類到了其他分類。

(1)partition list (all)

通路列舉分區(list partition)表的所有分區。

當查詢的資料可能會出現在所有分區上時,需要列舉所有分區。

(2)partition list (single)

僅通路列舉分區(list partition)表的一個分區。

分區裁剪(partition pruning):在對分區表進行查詢時,優化器會檢查謂詞條件中是否存在對分區字段的過濾,如果存在,則可以僅通路符合條件的分區,即裁剪掉沒必要通路的分區,進而提高效率。

hellodba.com>exec sql_explain('select * from t_objects_list where owner = :a', 'typical');

| 0 | select statement | | 7871 | 676k| 9 (0)| 00:00:10 | | |

| 1 | partition list single| | 7871 | 676k| 9 (0)| 00:00:10 | key | key|

(3)partition list (inlist)

通路列舉分區(list partition)表的所有與分區字段in條件比對的分區。

(4)partition list (iterator)

對列舉分區(list partition)表中符合分區字段範圍比對條件的分區進行疊代。

(5)partition list (or)

通路列舉分區(list partition)中符合兩個或多個分區字段比對條件之一的分區。

(6)partition list (subquery)

通路列舉分區(list partition)表中所有與子查詢條件比對的分區。示例如代碼清單2-12所示。

代碼清單2-12 partition list (subquery)示例

hellodba.com>exec sql_explain('select /+use_hash(tp) x_dyn_prune/ tp. from t_objects_list tp where

| 0 | select statement | | 7871 | 822k| 26 (4)| 00:00:26 | | |

|* 1 | hash join | | 7871 | 822k| 26 (4)| 00:00:26 | | |

| 2 | table access by index rowid| t_users | 1 | 19 | 1 (0)| 00:00:02 | | |

|* 3 | index unique scan | t_users_pk | 1 | | 1 (0)| 00:00:02 | | |

| 4 | partition list subquery | | 23614 | 2029k| 24 (0)| 00:00:25 |key(sq)|key(sq)|

1 - access("tp"."owner"="t4"."username")

3 - access("t4"."user_id"=to_number(:a))

(7)partition range (all)

通路範圍分區(range partition)表的所有分區。

(8)partition range (single)

僅通路範圍分區(range partition)表的一個分區。

(9)partition range (inlist)

通路範圍分區(range partition)表的所有與分區字段in條件比對的分區。

(10)partition range (iterator)

對範圍分區(range partition)表中符合分區字段比對條件的分區進行疊代。

(11)partition range (or)

通路範圍分區(range partition)表中符合兩個或多個分區字段比對條件之一的分區。

(12)partition range (subquery)

通路範圍分區(range partition)表的所有與子查詢條件相比對的分區。

(13)partition range (multi-column)

通路以組合字段為分區鍵的範圍分區(range partition)表中所有與多個分區字段過濾條件相比對的分區。示例如下:

hellodba.com>exec sql_explain('select tr.* from t_objects_range tr where tr.owner<:a and

| 0 | select statement | | 59 | 5782 | 26 (0)| 00:00:27 | | |

| 1 | partition range multi-column| | 59 | 5782 | 26 (0)| 00:00:27 |key(mc)|key(mc)|

2 - filter("tr"."owner"<:a and "tr"."object_name"<:b)

(14)partition hash (all)

通路哈希分區(hash partition)表的所有分區。

(15)partition hash (single)

僅通路哈希分區(hash partition)表的一個分區。

(16)partition hash (inlist)

通路哈希分區(hash partition)表的所有與分區字段in條件相比對的分區。

(17)partition hash (iterator)

對哈希分區(hash partition)表中符合分區字段比對條件的分區進行疊代。示例如代碼清單2-13所示。

提示:僅當通路有限個分區時,優化器會考慮對分區進行疊代。由于雜湊演算法的特性,即哈希值的均勻分布性,對分區字段的範圍比對可能會通路到所有分區,是以這種情況下優化器不會對分區疊代。

代碼清單2-13 partition hash (iterator)示例

hellodba.com>exec sql_explain('select th.* from t_objects_hash th where owner in (select username from

| 0 | select statement | | 576 | 66240 | 7 (15)| 00:00:08 | | |

| 1 | nested loops | | 576 | 66240 | 7 (15)| 00:00:08 | | |

| 2 | view | vw_nso_1 | 3 | 51 | 2 (0)| 00:00:03 | | |

| 3 | hash unique | | 1 | 51 | | | | |

|* 4 | count stopkey | | | | | | | |

| 5 | table access full | t_users | 41 | 697 | 2 (0)| 00:00:03 | | |

| 6 | partition hash iterator| | 576 | 56448 | 4 (0)| 00:00:05 | key | key |

4 - filter(rownum<=3)

7 - filter("owner"="$nso_col_1")

2.4.10 并行查詢操作

下面介紹與并行查詢相關的操作。

并行查詢:并行查詢是通過将查詢操作任務分成若幹個子任務,交由多個并行服務程序(或線程)執行,以提高處理速度的方法。

并行度(degree of parallelism,dop):與單個操作關聯的并行服務程序數即為并行度。并行度可以通過建立對象的ddl語句指定,也可以通過語句中的提示指定。

通常,采用并行查詢的目的是減少查詢響應時間,而不是以降低傳統性能名額(如io、cpu)為度量标準(相反,這些資料可能會比串行執行情況下更高)。

提示:參數parallel_max_servers控制最大并行服務程序數,parallel_min_servers控制最小并行服務程序數,parallel_max_servers為1則禁用并行查詢。

(1)px coordinator

并行執行協調者,也為查詢協調者(query coordinator,qc)。在并行查詢語句的執行計劃中,px coordinator是執行并行操作的第一步。

在并行執行過程中,并行執行協調者是一個獨立的會話(即使用者會話本身),負責并行語句的初始化,并且将可并行操作分解,按照一定政策将分解後的子任務分發給并行服務會話。此外,它還承擔一部分無法并行操作的任務。

(2)px send qc (random)

并行服務程序通過表隊列(table queue)将資料随機發送給協調者。

表隊列(table queue,tq):表隊列是協調者與并行服務程序之間、并行程序互相之間的資料傳輸通道。執行計劃中的tq字段顯示了目前操作中程序之間通信所用的表隊列資訊。

示例如代碼清單2-14所示。

代碼清單2-14 px coordinator和px send qc示例

hellodba.com>alter system set parallel_max_servers=20 scope=memory;

system altered.

hellodba.com>exec sql_explain('select /+parallel(t 2)/* from t_objects t', 'typical');

| 0 | select statement | | 47585 | 5436k| 30 (0)| 00:00:31 | | | |

| 1 | px coordinator | | | | | | | | |

| 2 | px send qc (random)| :tq10000 | 47585 | 5436k| 30 (0)| 00:00:31 | q1,00 | p->s | qc (rand) |

| 3 | px block iterator | | 47585 | 5436k| 30 (0)| 00:00:31 | q1,00 | pcwc | |

(3)px send qc (order)

并行服務程序通過表隊列(table queue)将資料按序發送給協調者。

(4)px receive

消費者(consumer)通過表隊列(table queue)接收從生産者(producer)發送過來的資料。

生産者(producer)/消費者(consumer)模式:為了提高資料處理效率,并行程序被分成程序集,成對協同工作:一組負責“生産”資料行,稱為“生産者”(producer);另一組則“消費”這些資料行,稱為“消費者”。例如,在進行關聯(join)查詢時,一組程序集從一張表中讀取資料,并通過表隊列(table queue)通道發送資料,這組程序集就是生産者;另外一組程序集則從隊列通道中接收資料,并将資料與從另外一張表的資料進行關聯,這組程序集就是消費者。

在生産者/消費者模式下,實際需要的并行服務程序數量是指定并行度(dop)的兩倍。除了對單個表的簡單查詢外,大多數并行執行語句都會運作在生産者/消費者模式下。

(5)px send (range)

生産者(producer)依照資料範圍将資料分發給不同消費者(consumer)。

資料分發(distribution):在并行查詢中,每一個生産者程序都隻會擷取互相之間不重疊的一部分資料,而消費者在執行某些操作(如關聯)時,需要從多個生産者擷取資料,生産者就需要按照一定方式将資料分發給需要其資料的消費者。執行計劃中,pq distrib字段表示分發方式。

在并行查詢中,同一個操作可能會有多個程序同時進行,是以,在執行計劃中需要描述父子操作之間關系,字段in-out就是顯示該資訊。并行操作之間關系包括:

并行至串行(parallel to serial,p->s):多個程序同時執行的并行操作向單個程序執行的串行操作發送資料,其資料的發送、接收需要通過表隊列(table queue)完成。如并行服務程序向協調者發送資料。

并行至并行(parallel to parallel,p->p):多個程序同時執行的并行操作向多個程序同時執行的并行操作發送資料,其資料的發送、接收需要通過表隊列(table queue)完成。如多個生産者程序向多個消費者程序分發資料。

串行至并行(serial to parallel,s->p):單個程序執行的串行操作向多個程序同時執行的并行操作發送資料,其資料的發送、接收需要通過表隊列(table queue)完成。如某些情況下,并行程序的并發操作的子操作無法并行執行,或者子操作的對象太小,并行化代價大于串行代價。

與父操作捆綁進行的并行操作(parallel combined with parent,pcwp):父子操作都是并行操作,但必須由同一程序完成。

與子操作捆綁進行的并行操作(parallel combined with child,pcwc):父子操作都是并行操作,但必須由同一程序完成。

示例如代碼清單2-15所示。

代碼清單2-15 px send qc (order)、px receive和px send (range)示例

hellodba.com>exec sql_explain('select /+parallel(t 2)/* from t_tables t order by table_name',

| 0 | select statement | | 2070 | 412k| | 47 (3)| 00:00:47 | | | |

| 1 | px coordinator | | | | | | | | | |

| 2 | px send qc (order) | :tq10001 | 2070 | 412k| | 47 (3)| 00:00:47 | q1,01 | p->s | qc (order) |

| 3 | sort order by | | 2070 | 412k| 1288k| 47 (3)| 00:00:47 | q1,01 | pcwp | |

| 4 | px receive | | 2070 | 412k| | 3 (0)| 00:00:04 | q1,01 | pcwp | |

| 5 | px send range | :tq10000 | 2070 | 412k| | 3 (0)| 00:00:04 | q1,00 | p->p | range |

| 6 | px block iterator | | 2070 | 412k| | 3 (0)| 00:00:04 | q1,00 | pcwc | |

(6)px send (broadcast)

生産者(producer)将資料廣播分發給所有消費者(consumer)。

(7)px send (broadcast local)

生産者(producer)将資料廣播分發給所有操作于同一分區的消費者(consumer)。

(8)px send (hash)

生産者(producer)依照哈希值将資料分發給相應的消費者(consumer)。

(9)px send (hash (block address))

生産者(producer)依照哈希值(由資料塊位址而不是資料記錄中的字段數值計算得出)将資料分發給相應的消費者(consumer)。示例如代碼清單2-16所示。

提示:如果要使dml語句并行化,需要激活目前會話的并行dml屬性。

代碼清單2-16 px send (hash (block address))示例

hellodba.com>alter session enable parallel dml;

session altered.

hellodba.com>exec sql_explain('delete /+parallel(o)/from t_objects o where exists (select 1 from

| 0 | delete statement | | 23 | 2300 | 15 (0)| 00:00:16 | | | |

| 1 | px coordinator | | | | | | | | |

| 2 | px send qc (random) | :tq10002 | 23 | 2300 | 15 (0)| 00:00:16 | q1,02 | p->s | qc (rand) |

| 3 | index maintenance | t_objects | | | | | q1,02 | pcwp | |

| 4 | px receive | | 23 | 2300 | 15 (0)| 00:00:16 | q1,02 | pcwp | |

| 5 | px send range | :tq10001 | 23 | 2300 | 15 (0)| 00:00:16 | q1,01 | p->p | range |

| 6 | delete | t_objects | | | | | q1,01 | pcwp | |

| 7 | px receive | | 23 | 2300 | 15 (0)| 00:00:16 | q1,01 | pcwp | |

| 8 | px send hash (block address)| :tq10000 | 23 | 2300 | 15 (0)| 00:00:16 | q1,00 | p->p | hash (block|

| 9 | nested loops semi | | 23 | 2300 | 15 (0)| 00:00:16 | q1,00 | pcwp | |

| 10 | px block iterator | | | | | | q1,00 | pcwc | |

| 11 | table access full | t_objects | 47585 | 3531k| 15 (0)| 00:00:16 | q1,00 | pcwp | |

12 - access("t"."table_name"="o"."object_name" and "t"."owner"="o"."owner")

(10)px send (hybrid (rowid pkey))

生産者(producer)依照rowid和主鍵将資料分發給相應的消費者(consumer)。示例如代碼清單2-17所示。

代碼清單2-17 px send (hybrid (rowid pkey))示例

hellodba.com>exec sql_explain('merge /+parallel(t 6) full(t)/ into t_xpl t using (select

plan hash value: 2874460846

| 0 | merge statement | | 47585 | 11m| 57 (2)| 00:00:57 | | | |

| 1 | px coordinator | | | | | | | | |

| 2 | px send qc (random) | :tq10004 | 47585 | 11m| 57 (2)| 00:00:57 | q1,04 | p->s | qc (rand) |

| 3 | index maintenance | t_xpl | | | | | q1,04 | pcwp | |

| 4 | px receive | | 47585 | 11m| 57 (2)| 00:00:57 | q1,04 | pcwp | |

| 5 | px send range | :tq10003 | 47585 | 11m| 57 (2)| 00:00:57 | q1,03 | p->p | range |

| 6 | merge | t_xpl | | | | | q1,03 | pcwp | |

| 7 | px receive | | 47585 | 11m| 57 (2)| 00:00:57 | q1,03 | pcwp | |

| 8 | px send hybrid (rowid pkey)| :tq10002 | 47585 | 11m| 57 (2)| 00:00:57 | q1,02 | p->p | hybrid(row|

| 9 | view | | | | | | q1,02 | pcwp | |

|* 10 | hash join right outer | | 47585 | 11m| 57 (2)| 00:00:57 | q1,02 | pcwp | |

| 11 | px receive | | 1 | 226 | 2 (0)| 00:00:03 | q1,02 | pcwp | |

| 12 | px send hash | :tq10001 | 1 | 226 | 2 (0)| 00:00:03 | q1,01 | p->p | hash |

| 13 | px block iterator | | 1 | 226 | 2 (0)| 00:00:03 | q1,01 | pcwc | |

| 14 | table access full | t_xpl | 1 | 226 | 2 (0)| 00:00:03 | q1,01 | pcwp | |

| 15 | buffer sort | | | | | | q1,02 | pcwc | |

| 16 | px receive | | 47585 | 1626k| 54 (0)| 00:00:55 | q1,02 | pcwp | |

| 17 | px send hash | :tq10000 | 47585 | 1626k| 54 (0)| 00:00:55 | | s->p | hash |

10 - access("t"."tname"(+)="object_name")

(11)px send (partition (key))

以分區為劃分粒度,生産者(producer)依照分區鍵值将資料發送給相應的消費者(consumer)。示例如代碼清單2-18所示。

并行顆粒(granule):并行進行中的最小工作機關。oracle将可并行操作(如表掃描)劃分為若幹個顆粒,并行服務程序每次執行一個顆粒的操作。包含資料塊範圍(block range)顆粒和分區(partition)顆粒:

資料塊範圍(block range)顆粒:資料塊範圍顆粒是并行操作中的最基本顆粒。并行服務程序每次讀取或操作一段連續的資料塊。

分區(partition)顆粒: 每個并行服務程序操作一個分區或子分區。是以,這種顆粒劃分情況下,分區數量決定了最大并行度。通常在對本地分區索引進行範圍掃描或者分區表之間進行關聯時可能會以分區為粒度。

代碼清單2-18 px send (partition (key))示例

hellodba.com>exec sql_explain('select /+parallel(t 4) parallel(o 4) pq_distribute(t none partition)/*

plan hash value: 3990730760

| 0 | select statement | | 7808k| 2204m| 9 (0)| 00:00:10 | | | | | |

| 1 | px coordinator | | | | | | | | | | |

| 2 | px send qc (random) | :tq10001 | 7808k| 2204m| 9 (0)| 00:00:10 | | | q1,01 | p->s | qc (rand) |

|* 3 | hash join buffered | | 7808k| 2204m| 9 (0)| 00:00:10 | | | q1,01 | pcwp | |

| 4 | px partition list all | | 992 | 201k| 2 (0)| 00:00:03 | 1 | 3 | q1,01 | pcwc | |

| 5 | table access full | t_tables_list | 992 | 201k| 2 (0)| 00:00:03 | 1 | 3 | q1,01 | pcwp | |

| 6 | px receive | | 23614 | 2029k| 7 (0)| 00:00:07 | | | q1,01 | pcwp | |

| 7 | px send partition (key)| :tq10000 | 23614 | 2029k| 7 (0)| 00:00:07 | | | q1,00 | p->p | part (key) |

| 8 | px block iterator | | 23614 | 2029k| 7 (0)| 00:00:07 | 1 | 3 | q1,00 | pcwc | |

3 - access("o"."owner"="t"."owner")

(12)px send (round-robin)

串行程序以輪詢方式将資料分發給并行服務程序。示例如代碼清單2-19所示。

輪詢(round robin):輪詢是一種最簡單的資源選取方式。每一次從一組資源隊列中選取一個,且為上一次所選取資源的下一個資源,如果選取到了隊列的末尾,則從第一個繼續輪詢。

代碼清單2-19 px send (round-robin)示例

hellodba.com>exec sql_explain('insert /+parallel(t_xpl)/into t_xpl(tid) select user_id from

| 0 | insert statement | | 41 | 82 | 1 (0)| 00:00:02 | | | |

| 1 | px coordinator | | | | | | | | |

| 2 | px send qc (random) | :tq10001 | 41 | 82 | 1 (0)| 00:00:02 | q1,01 | p->s | qc (rand) |

| 3 | load as select | t_xpl | | | | | q1,01 | pcwp | |

| 4 | buffer sort | | | | | | q1,01 | pcwc | |

| 5 | px receive | | 41 | 82 | 1 (0)| 00:00:02 | q1,01 | pcwp | |

| 6 | px send round-robin| :tq10000 | 41 | 82 | 1 (0)| 00:00:02 | | s->p | rnd-robin |

(13)px block (iterator)

以資料塊範圍為劃分粒度,對一段範圍的資料塊進行疊代。

(14)px partition (list all)

以分區為劃分粒度,并行服務程序通路一個列舉分區,所有分區都會被通路。

(15)px partition (range (all))

以分區為劃分粒度,并行服務程序通路一個範圍分區,所有分區都會被通路。

(16)px partition (multi-column)

以分區為劃分粒度,并行服務程序通路一個以多字段為分區鍵的範圍分區,并對分區鍵做裁剪,隻通路比對的分區。

(17)px partition (hash (all))

以分區為劃分粒度,并行服務程序通路一個哈希分區,所有分區都會被通路。

(18)hash join (buffered)

生産者(producer)将資料分發給相同哈希值的消費者(consumer),由消費者在私有記憶體中對資料進行哈希關聯。

(19)hash join (right semi buffered)

生産者(producer)将資料分發給相同哈希值的消費者(consumer),由消費者在私有記憶體中對資料進行哈希不完整關聯操作。示例如代碼清單2-20所示。

代碼清單2-20 hash join (right semi buffered)示例

hellodba.com>exec sql_explain('select /+parallel(o 2)/* from t_objects o where exists (select

| 0 | select statement | | 23 | 3243 | 33 (4)| 00:00:33 | | | |

| 1 | px coordinator | | | | | | | | |

| 2 | px send qc (random) | :tq10002 | 23 | 3243 | 33 (4)| 00:00:33 | q1,02 | p->s | qc (rand) |

|* 3 | hash join right semi buffered| | 23 | 3243 | 33 (4)| 00:00:33 | q1,02 | pcwp | |

| 4 | buffer sort | | | | | | q1,02 | pcwc | |

| 5 | px receive | | 2070 | 49680 | 2 (0)| 00:00:03 | q1,02 | pcwp | |

| 6 | px send hash | :tq10000 | 2070 | 49680 | 2 (0)| 00:00:03 | | s->p | hash |

| 7 | index fast full scan | t_tables_pk | 2070 | 49680 | 2 (0)| 00:00:03 | | | |

| 8 | px receive | | 47585 | 5436k| 30 (0)| 00:00:31 | q1,02 | pcwp | |

| 9 | px send hash | :tq10001 | 47585 | 5436k| 30 (0)| 00:00:31 | q1,01 | p->p | hash |

| 10 | px block iterator | | 47585 | 5436k| 30 (0)| 00:00:31 | q1,01 | pcwc | |

3 - access("o"."owner"="t"."owner" and "o"."object_name"="t"."table_name")

(20)hash join (right anti buffered)

生産者(producer)将資料分發給相同哈希值的消費者(consumer),由消費者在私有記憶體中對資料進行哈希反關聯操作。

2.4.11 聚合操作

此類操作對資料集進行聚合,擷取相應資料。

(1)hash (group by)

通過哈希計算對數值進行分組。

提示:在hash(group by)出現(10gr2)之前,在對資料進行分組時,是先将資料排序,然後再擷取分組資料,即sort(group by)。要啟用hash(group by),需要確定優化器參數_gby_hash_aggregation_enabled為true。

hellodba.com>alter session set "_gby_hash_aggregation_enabled"=true;

hellodba.com>exec sql_explain('select owner, count(1) from t_tables group by owner','typical');

plan hash value: 3517041855

| 0 | select statement | | 21 | 126 | 3 (34)| 00:00:04 |

| 1 | hash group by | | 21 | 126 | 3 (34)| 00:00:04 |

(2)hash (unique)

通過哈希計算擷取唯一數值。

(3)generate (cube)

生成cube。cube可以在多個次元上,以及次元之間的組合上做聚集計算。

hellodba.com>exec sql_explain('select owner, tablespace_name, count(1) from t_tables group by

| 0 | select statement | | 149 | 1937 | 7 (15)| 00:00:08 |

| 1 | sort group by | | 149 | 1937 | 7 (15)| 00:00:08 |

| 2 | generate cube | | 149 | 1937 | 7 (15)| 00:00:08 |

| 3 | sort group by | | 149 | 1937 | 7 (15)| 00:00:08 |

2.4.12 分析函數操作

下面介紹用于計算分析函數結果的操作。

(1)window (buffer)

分析函數(analytic function)在視窗記憶體中進行聚集計算。

分析函數(analytic function):基于一組資料計算出聚集結果。這樣一組資料就稱為一個視窗(window),由分析函數中的over ( ... )子句定義。

通過分析函數可以避免由複雜的執行操作(如關聯)得出分析資料。示例如下:

hellodba.com>exec sql_explain('select table_name, count(1) over (partition by table_name) cnt from

plan hash value: 4083014614

| 0 | select statement | | 2070 | 37260 | 7 (0)| 00:00:07 |

| 1 | window buffer | | 2070 | 37260 | 7 (0)| 00:00:07 |

(2)window (sort)

分析函數(analytic function)在視窗記憶體中對資料排序後,再進行聚集計算。

(3)window (nosort)

分析函數(analytic function)在視窗記憶體中對資料按照資料的實體順序(或者資料結果有順序要求,但資料實體順序與邏輯順序一緻而無需再做排序)進行聚集計算。

(4)window (sort pushed rank)

子查詢中的分析函數(analytic function)在視窗記憶體中對資料排序時,主查詢中的謂詞條件被推入子查詢或視圖中序列函數上,作為子查詢或視圖的過濾條件。示例如下:

hellodba.com>exec sql_explain('select from (select /+index_ffs(t t_tables_idx1)*/owner, row_number()

| 0 | select statement | | 2070 | 62100 | 3 (34)| 00:00:04 |

|* 1 | view | | 2070 | 62100 | 3 (34)| 00:00:04 |

|* 2 | window sort pushed rank| | 2070 | 12420 | 3 (34)| 00:00:04 |

1 - filter("rnum"<=10)

2 - filter(row_number() over ( order by "owner")<=10)

從以上執行計劃中的謂詞資訊中可以看到push之後的過濾條件。

(5)window (nosort stopkey)

分析函數(analytic function)在視窗記憶體中按照實體順序處理資料時,處理特定記錄數後停止。示例如下:

hellodba.com>exec sql_explain('select from (select /+index(t t_tables_idx1)*/owner, row_number()

| 0 | select statement | | 2070 | 62100 | 4 (0)| 00:00:04 |

|* 1 | view | | 2070 | 62100 | 4 (0)| 00:00:04 |

|* 2 | window nosort stopkey| | 2070 | 12420 | 4 (0)| 00:00:04 |

(6)window(in sql model (sort))

依據模型化sql語句中的規則,建立視窗,對資料排序,進行聚集計算。示例如下:

hellodba.com>begin

2 sql_explain('select statistic#, s

3 from t_sesstat

4 model return updated rows

5 partition by (statistic#)

6 dimension by (sid)

7 measures (value v, 1 s)

8 rules

9 (

10 s[any] = sum(v) over (partition by statistic#)

11 )','typical');

12 end;

13 /

| 0 | select statement | | 7280 | 65520 | 3 (0)| 00:00:04 |

| 1 | sql model ordered | | 7280 | 65520 | | |

| 2 | table access full | t_sesstat | 7280 | 65520 | 3 (0)| 00:00:04 |

2.4.13 模型化操作

該類操作是對資料集進行模型化(model)處理的操作。

模型化sql語句(sql model)可以在一個或多個資料次元(dimesion)上,使用各種公式建立規則(rules),實作類似電子表格的計算功能。

(1)sql model (acyclic)

執行模型化sql語句,按照規則在rules子句中出現的順序,并遵循規則的定義,使用分析函數進行計算。示例如下:

2 sql_explain('select sid, statistic#, v,s

4 model

5 partition by (sid)

6 dimension by (statistic#)

8 rules automatic order

10 s[any] = v[currentv()]*1.05

11 )

12 order by sid, statistic#','typical');

13 end;

14 /

| 0 | select statement | | 7280 | 65520 | | 30 (4)| 00:00:31 |

| 1 | sort order by | | 7280 | 65520 | 296k| 30 (4)| 00:00:31 |

| 2 | sql model acyclic | | 7280 | 65520 | | 30 (4)| 00:00:31 |

(2)sql model (acyclic fast)

執行模型化sql語句,oracle自動選擇rules子句的規則順序,并遵循規則的定義,快速通路其他資料行(或依據規則計算出來的資料行),還使用分析函數進行計算。示例如下:

hellodba.com> begin

2 sql_explain('select sid,statistic#,v

7 measures (value v)

10 v[999] = v[52]+v[64]

12 order by sid, statistic#','typical');

13 end;

plan hash value: 16555562

| 0 | select statement | | 7280 | 65520 | | 30 (4)| 00:00:31 |

| 1 | sort order by | | 7280 | 65520 | 296k| 30 (4)| 00:00:31 |

| 2 | sql model acyclic fast| | 7280 | 65520 | | 30 (4)| 00:00:31 |

(3)sql model (ordered)

執行模型化sql語句,按照規則在rules子句中出現的順序,并遵循規則的定義,對資料進行計算。示例如下:

8 rules sequential order

| 0 | select statement | | 7280 | 65520 | 3 (0)| 00:00:04 |

| 1 | sql model ordered | | 7280 | 65520 | | |

pl/sql procedure successfully completed.

(4)sql model (ordered fast)

執行模型化sql語句,按照規則在rules子句中出現的順序,并遵循規則的定義,快速通路其他資料行(或依據規則計算出來的資料行),對資料進行計算。示例如下:

| 2 | sql model ordered fast| | 7280 | 65520 | | 30 (4)| 00:00:31 |

(5)reference model

執行模型化sql語句,并且引用其他的模型化sql語句。示例如代碼清單2-21所示。

引用模型(reference model)作為主模型(main model)的次元,可以有一到多個,并且為隻讀,即主模型可以在規則中引用它們的資料單元,但是不能修改它們的資料。

代碼清單2-21 reference model示例

2 sql_explain('select owner, tablespace_name, tbcnt, tt

3 from t_tables

4 group by owner,tablespace_name

5 model

6 reference usr on

7 (

8 select username, default_tablespace from t_users

9 )

10 dimension by (username)

11 measures (default_tablespace d) ignore nav

12 main tab

13 dimension by (owner, tablespace_name)

14 measures (count(1) tbcnt, lpad(''a'',30) tt) ignore nav

15 rules

16 (

17 tt[any,any] = d[cv(owner)]

18 )

19 order by owner, tablespace_name','typical');

20 end;

21 /;

| 0 | select statement | | 149 | 1937 | 8 (25)| 00:00:09 |

| 1 | sort order by | | 149 | 1937 | 8 (25)| 00:00:09 |

| 2 | sql model ordered | | 149 | 1937 | 8 (25)| 00:00:09 |

| 3 | reference model | usr | 41 | 1394 | 2 (0)| 00:00:03 |

| 4 | table access full| t_users | 41 | 1394 | 2 (0)| 00:00:03 |

| 5 | hash group by | | 149 | 1937 | 8 (25)| 00:00:09 |

2.4.14 資料和對象管理操作

此類操作會造成資料或對象的改變。

(1)multi-table insert

将資料同時插入多個表中,示例如下:

hellodba.com>exec sql_explain('insert all into t_tables_sub1 into t_tables_sub2 select * from

| 0 | insert statement | | 2070 | 412k| 6 (0)| 00:00:07 |

| 1 | multi-table insert | | | | | |

| 2 | into | t_tables_sub1 | | | | |

| 3 | into | t_tables_sub2 | | | | |

(2)into

多表插入時,将資料插入其中一張表。

(3)direct load into

将資料直接載入表中。

提示:直接載入資料時,無論高水位線(high water mark)以下是否存在空閑資料塊或未使用的資料塊,都會配置設定新的擴充段給新插入的資料。

hellodba.com>exec sql_explain('insert /+append/all into t_tables_sub1 into t_tables_sub2 select *

| 2 | direct load into | t_tables_sub1 | | | | |

| 3 | direct load into | t_tables_sub2 | | | | |

(4)index build (non unique)

建構非唯一索引。

(5)index build (unique)

建構唯一索引。

(6)index build (non unique (local))

建構本地非唯一索引。

(7)index build (unique (local))

建構本地唯一索引。

注意,本地唯一索引必須包含分區字段。

(8)index maintenance

在進行并行dml操作時,維護表中的索引。示例如代碼清單2-22所示。

代碼清單2-22 index maintenance示例

hellodba.com>exec sql_explain('delete /+parallel(o)/from t_xpl o','typical');

| 0 | delete statement | | 1 | 25 | 2 (0)| 00:00:03 | | | |

| 1 | px coordinator | | | | | | | | |

| 2 | px send qc (random) | :tq10001 | 1 | 25 | 2 (0)| 00:00:03 | q1,01 | p->s | qc (rand) |

| 3 | index maintenance | t_xpl | | | | | q1,01 | pcwp | |

| 4 | px receive | | 1 | 25 | 2 (0)| 00:00:03 | q1,01 | pcwp | |

| 5 | px send range | :tq10000 | 1 | 25 | 2 (0)| 00:00:03 | q1,00 | p->p | range |

| 6 | delete | t_xpl | | | | | q1,00 | pcwp | |

| 7 | px block iterator | | 1 | 25 | 2 (0)| 00:00:03 | q1,00 | pcwc | |

注意,如果被更新的表中不存在索引,則這個操作不會出現在執行計劃中。

(9)bitmap construction

在建立位圖索引過程中構造位圖。

2.4.15 其他操作

所有未歸類的操作均放入該類。

(1)count

通過計數器對擷取到的資料記錄計數,以産生僞列rownum的數值。示例如下:

hellodba.com>exec sql_explain('select owner, table_name, rownum from t_tables','typical');

| 0 | select statement | | 2070 | 49680 | 2 (0)| 00:00:03 |

| 1 | count | | | | | |

(2)count (stopkey)

通過計數器對擷取到的資料記錄計數,以産生僞列rownum的數值,并且當記錄數達到條件時停止計數和資料的讀取。

(3)inlist iterator

對in條件中的數值進行疊代,分别通過索引擷取滿足條件的資料集。

(4)filter

對已經讀取的資料集進行過濾。

提示:對于查詢中的條件,可以分為兩類條件:一類為通路條件,即通過該條件可以定位到資料的實體位置,例如索引字段比對;另一類為過濾條件,即需要在擷取資料(fetch)時再次進行過濾,以滿足查詢條件。而我們這裡的過濾,則是對一個已經讀取到的結果集再次進行過濾。

hellodba.com>exec sql_explain('select owner, count(1) from t_tables group by owner having

| 0 | select statement | | 2 | 12 | 3 (34)| 00:00:04 |

|* 1 | filter | | | | | |

| 2 | sort group by | | 2 | 12 | 3 (34)| 00:00:04 |

1 - filter(count(*)<10)

(5)first row

僅讀取通路到的第一條資料,并立即傳回。

通過索引讀取前導索引字段的最大值或最小值時,由于資料已經排序,是以第一條資料就是需要的資料,無需再讀取剩餘資料。示例如下:

hellodba.com>exec sql_explain('select max(table_name) from t_tables where table_name < :a','typical');

plan hash value: 1276389898

| 0 | select statement | | 1 | 18 | 1 (0)| 00:00:02 |

| 1 | sort aggregate | | 1 | 18 | | |

| 2 | first row | | 104 | 1872 | 1 (0)| 00:00:02 |

3 - access("table_name"<:a)

(6)for update

将讀取到的資料記錄加鎖,等待事務的後續語句對資料進行更新。

(7)collection iterator (constructor fetch)

對一個構造出來的集合對象中的成員進行疊代取值。

從集合對象中查詢資料,需要通過表映射函數(table)進行映射。示例如下:

hellodba.com>exec sql_explain('select * from

| 0 | select statement | |

(8)collection iterator(pickler fetch)

對一個集合對象中的成員進行疊代取值。示例如下:

hellodba.com>exec sql_explain('select * from table(get_phonelist())','typical');

| 0 | select statement | |

(9)collection iterator (subquery fetch)

将一個子查詢結果集映射成集合,并對集合對象中的成員進行疊代取值。示例如下:

hellodba.com>exec sql_explain('select /+no_merge(t)/* from table(cast(multiset(select

| 0 | select statement | | 8168 | 207k| 8 (0)| 00:00:09 |

| 1 | view | | 8168 | 207k| 8 (0)| 00:00:09 |

| 2 | collection iterator subquery fetch| | | | | |

(10)sequence

通路序列(sequence)對象。

(11)remote

通路遠端資料庫上的對象。示例如下:

hellodba.com>create database link ora10201 connect to demo identified by demo using 'ora10201';

database link created.

hellodba.com>exec sql_explain('select from t_users@ora10201 union select from t_users','typical');

| 0 | select statement | | 82 | 7380 | 6 (67)| 00:00:07 | | |

| 1 | sort unique | | 82 | 7380 | 6 (67)| 00:00:07 | | |

| 2 | union-all | | | | | | | |

| 3 | remote | t_users | 41 | 3690 | 2 (0)| 00:00:03 | ora10~ | r->s |

3 - select "username","user_id","password","account_status","lock_date","expiry_date

提示:在分布式查詢的執行計劃中,會多出兩列資料,inst為遠端對象所在執行個體名,in-out為資料傳輸方式,r->s表示由遠端傳向本地串行操作。

(12)sequence (remote)

通路遠端資料庫上的序列(sequence)對象。

(13)temp table transformation

對語句執行過程中産生的臨時表進行轉換。示例如代碼清單2-23所示。

代碼清單2-23 temp table transformation示例

hellodba.com>exec sql_explain('select 1 from dual group by cube(1,1)', 'typical');

| 0 | select statement | | 1 | 13 | 10 (0)| 00:00:11 |

| 1 | temp table transformation | | | | | |

| 2 | multi-table insert | | | | | |

| 3 | direct load into | sys_temp_0fd9d662a_eecffe34 | | | | |

| 4 | direct load into | sys_temp_0fd9d662b_eecffe34 | | | | |

| 5 | sort group by nosort rollup| | 1 | | 2 (0)| 00:00:03 |

| 6 | fast dual | | 1 | | 2 (0)| 00:00:03 |

| 7 | view | | 4 | 52 | 8 (0)| 00:00:09 |

| 8 | view | | 4 | 52 | 8 (0)| 00:00:09 |

| 9 | union-all | | | | | |

| 10 | table access full | sys_temp_0fd9d662a_eecffe34 | 1 | 13 | 2 (0)| 00:00:03 |

| 11 | table access full | sys_temp_0fd9d662a_eecffe34 | 1 | 13 | 2 (0)| 00:00:03 |

| 12 | table access full | sys_temp_0fd9d662a_eecffe34 | 1 | 13 | 2 (0)| 00:00:03 |

(14)approximate ndv aggregate

唯一值數(number of distinct value,ndv)估計。這是11g中新出現的操作,基于雜湊演算法對字段的ndv數值進行估算(我們在後面章節會介紹這一算法)。并且該操作僅出現在對象分析過程中執行的相關語句中。示例如代碼清單2-24所示(其中遊标是在表分析過程中産生)。

代碼清單2-24 approximate ndv aggregate示例