您好,登錄后才能下訂單哦!
一、 表連接
顧名思義,表連接就是指多個表之間用連接條件連接在一起,使用表連接的目標SQL的目的就是從多個表獲取存儲在這些表中的不同維度的數據。體現在SQL語句上,含表連接的目標SQL的from部分會出現多個表,而這些SQL的where條件部分則會定義具體的表連接條件。
當優化器解析含表連接的目標SQL時,它除了會根據目標SQL的SQL文本的寫法來決定表連接的類型之外,還必須決定如下三件事情才能得到最終的執行計劃。
1.表連接順序
不管目標SQL中有多少個表做表連接,Oracle在實際執行該SQL時都只能先兩兩做表連接,再依次執行這樣的兩兩表連接過程,直到目標SQL中所有的表都已連接完畢。從嚴格意義上來說,這里的表連接順序包含兩層含義:一層含義是當兩個表做表連接時,優化器需要決定這兩個表中誰是驅動表(outer table),誰是被驅動表(inner table);另外一層含義是當多表(超過兩個以上的表)做表連接時,優化器需要決定這些表中誰和誰先做表連接,然后決定這個表連接結果所在的結果集再和剩余表中的哪一個再做表連接,這個兩兩表連接的過程會一直持續下去,直到目標SQL中所有的表都已經連接完為止。
2.表連接方法
在Oracle數據庫中,兩個表之間的表連接方法有排序合并(sort merge join)、嵌套循環連接(nested loops join)、哈希連接(hash join)和笛卡兒連接(cross join)這4種,所以優化器在解析含表連接的目標SQL時,都需要從上述四種方法中選擇一種,作為每一對表兩兩做表連接時所需要采用的方法。
3.訪問單表的方法
對于優化器而言,僅決定表連接順序和表連接方法是不夠的,這還不中以得到目標SQL的最終執行計劃,因為優化器在對目標SQL中的各個表兩兩做表連接時,還必須決定如何去獲取存儲在這些表里的不同維度的數據,即優化器還要決定訪問單表的方法。比如在訪問某個單表時,是采用全表掃描還是走索引,如果是走索引,應該采用什么樣的索引訪問方法等。
1.1 表連接的類型
通常情況下,我們可以認為Oracle數據庫中的表連接分為內連接和外連接這兩種類型,表連接的類型會直接決定表連接的結果,而目標SQL的SQL文本的寫法又直接決定了表連接的類型。
1.1.1 內連接
內連接(Inner Join)是指表連接的結果只包含那些完全滿足連接條件的記錄。對于包含表連接的目標SQL而言,只要其where條件中沒有寫那些標準SQL中定義或者Oracle中自定義的表示外連接的關鍵字(比如標準SQL中的left outer join、right outer join、full outer join,或者Oracle中自定義的用來表示外連接的關鍵字“(+)”),則該SQL的連接類型就是內連接。
Oracle自定義的內連接寫法:
目標表1,目標表2 where 連接條件
標準SQL中內連接是用JOIN ON或者JOIN USING。
JOIN ON的語法:
目標表1 join 目標表2 on (連接條件)
JOIN USING的語法:
目標表1 join 目標表2 using(連接列集合)。
對于使用JOIN USING的目標SQL而言,如果有多個連接列,其語法中“(連接列集合)”里的各個連接列之間應使用逗號來分隔。需要注意的時,使用JOIN USING的連接語法,如果連接列同時又出現在查詢列中,則該連接列前不能帶上表名或者表名的別名(alias),否則Oracle會報錯(ORA-25154)。
標準SQL中還有一種特殊的JOIN USING,我們稱之為NATURAL JOIN,其含義是使用NATURAL JOIN的表連接的連接列是表連接的兩個表所有的同名列。語法:
目標表1 natural join 目標表2
這實際相當于目標表1 join 目標表2 using(目標表1和目標表2的所有同名列集合)。使用NATURAL JOIN好外是無須寫連接列集合,但其壞處是增加了表連接的執行結果出錯的風險,因為兩個表之間的同名列不一定在含義上就完全相同,也許只是恰好同名,而即使含義相同,也不一定就需要將它們作為連接列。
1.1.2 外連接
外連接(Outer Join)是對內連接的一種擴展,它是指表連接的連接結果除了包含那些完全滿足連接條件的記錄之外還會包含驅動表中所有不滿足該條件的記錄。
標準SQL中的外連接分為左外連接(Left Outer Join)、右連接(Right Outer Join)和全連接(Full Outer Join)這三種,它們在標準SQL中對應的關鍵字分別為left outer join、right outer join、full outer join,都可以和JOIN ON 或JOIN USING連用。
左連接的語法:
目標表1 left outer join 目標表2 on(連接條件) 或目標表1 left outer join 目標表2 using (連接列集合)
其含義是目標表1和目標表2按括號中的連接條件來做表連接,位于關鍵字左邊的表1作為驅動表(outer table),此時的連接結果包含了表1和表2中所有滿足該連接條件的記錄外,還會包含驅動表(表1)中所有不滿足該連接條件的記錄,同時,驅動表中所有不滿足該連接條件的記錄所對應的被驅動表(表2)中的查詢列均會以NULL值來填充。
右連接的語法:
目標表1 right outer join 目標表2 on(連接條件) 或目標表1 right outer join 目標表2 using (連接列集合)
含義與左連接相似,不過,這次位于關鍵字右表的表2為驅動表。
全連接語法:
目標表1 full outer join 目標表2 on(連接條件) 或目標表1 full outer join 目標表2 using (連接列集合)
其含義是目標表1和目標表2按括號中的連接條件來做表連接。此時的連接結果除了包含表1和表2中所有滿足該連接條件的記錄外,還會包含目標表1和目標表2中所有不滿足該連接條件的記錄,同時,表1和表2中所有不滿足該連接條件的記錄所對就的另外一個表中的查詢列均會以NULL值來填充。
上面介紹的范例SQL中除了帶連接條件外,并沒有帶其他額外的限制條件。如果目標SQL中除了表連接條件之外還帶了額外的限制條件,則目標SQL中表連接的類型和該額外限制條件在目標SQL的SQL文本中出現的位置都可能會對最終執行結果產生影響。
內連接添加其他限制條件實例:
對內連接而言,除了表連接條件之外的額外限制條件在目標SQL的SQL文本中所處的位置不會影響該SQL的實際執行結果。
外連接添加其他限制條件實例:
對于外連接而言,如果額外限制條件在外連接關鍵字對應的括號內,這表示該限制條件會在表t1和表t2做右連接之前就被應用在表t1上,而如果額外限制條件在外連接關鍵字對應的括號外,表示該限制條件在表t1和表t2做完右連接后,才會被應用在表t1和表t2的連接結果集上。
所以,對于外連接而言,除了表連接條件之外的額外限制條件在目標SQL的SQL文本中所處的位置確實可能會影響該SQL的實際執行結果。
和標準SQL里表示外連接的語法不同,Oracle用自定義的關鍵字“(+)”來表示外連接。關鍵字“(+)”的位置在目標SQL連接條件中某一個表的連接列后面,其含義是關鍵字“(+)”出現在哪個表的連接列后面,就表明哪個表會以NULL值來填充那不滿足連接條件找位置該表中的查詢列,此時應該以關鍵字“(+)”對應的表作為外連接的驅動表,這是的關鍵是哪個表是驅動表!
之前提到過:對于外連接而言,表連接條件之外的額外限制條件在目標SQL的SQL文本中所處位置的不同可能會影響該SQL的實際執行結果。那如果使用Oracle自定義的關鍵字“(+)”來表示外連接的話,那么如何體現呢?很簡單,Oracle是通過在額外限制條件的目標列的后面帶上同樣的關鍵字“(+)”來體現出上述影響的:
select t1.col1,t1.col2,t2.col3
from t1,t2
where t1.col2(+)=t2.col2
and t1.col1(+)=1;
前面提到的NATURAL JOIN不僅適用于內連接,也同樣適用于外連接:
select t1.col1,col2,t2.col3
from t1 natural left outer join t2 ;
1.2 表連接的方法
之前介紹過,優化器在解析含表連接的目標SQL時,當它根據目標SQL的SQL文本的寫法決定表連接的類型之后,接下來要做的事情之一就是決定表連接的方法。
在Oracle數據庫中,兩個表之間的表連接方法有排序合并連接、嵌套循環連接、哈希連接和笛卡兒連接這四種。這四種表連接各有優缺點,也各有其適用場景,接下來分別介紹它們
1.2.1 排序合并連接
排序合并連接(Sort Merge Join)是一種兩個表在做表連接時用排序操作(Sort)和合并操作(Merge)來得到連接結果集的表連接方法。
如果兩個表(假如為T1和T2)做表連接時使用的是排序合并連接,則Oracle會依次順序執行如下步驟:
首先以目標SQL中指定的謂詞條件(如果有的話)去訪問表T1,然后對訪問結果按照表T1中的連接來排序,排好序后的結果集我們記為結果集1。
接著以目標SQL中指定的謂詞條件(如果有的話)去訪問表T2,然后對訪問結果按照表T2中的連接來排序,排好序后的結果集我們記為結果集2。
最后對結果集1和結果集2執行合并操作,從中取出匹配記錄來作為排序合并連接的最終執行結果。
對于排序合并連接的優缺點及適用場景,總結如下:
通常情況下,排序合并連接的執行效率會遠不如哈希連接,但前者的使用范圍更廣,因為哈希連接通常只能用于等值連接,而排序合并連接并不能用于其他條件(例如<、<=、>、>=)。
通常情況下,排序合并連接并不短途OLTP類型的系統,其本質原因是因為對OLTP類型的系統而言,排序是非常昂貴的操作,當然,如果能避免排序操作,那么即使是OLTP類型的系統,也還是可以使用排序合并連接的。比如兩個表雖然是排序合并連接,但實際上它們并不需要排序,因為這兩個表各自的連接列上都存在索引。
從嚴格意義上說,排序合并連接不存在驅動表的概念。
1.2.2 嵌套循環連接
嵌套循環連接(Nested Loops Join)是一種兩個表在做表連接時依靠兩層嵌套循環(分別為外層循環和內層循環)來得到連接結果集的表連接方法。
如果兩個表(假如為T1和T2)在做表連接時使用的是嵌套循環連接,則Oracle會依次順序執行如下步驟:
首先,優化器會按照一定的規則來決定表T1和T2中誰是驅動表、誰是被驅動表。驅動表用于外層循環,被驅動表用于內存循環。這是假設驅動表是T1,被驅動表是T2。
接著以目標SQL中指定的謂詞條件(如果有的話)去訪問驅動表T1,訪問驅動表T1后得到的結果集我們記為驅動結果集1。
然后遍歷驅動結果集1并同時遍歷被驅動表T2,即先取出驅動結果集1中的第1條記錄,接著遍歷被驅動表T2并按照連接條件去判斷T2中是否存在匹配的記錄,然后再取出驅動結果集1中的第2條記錄,按照同樣的連接條件再去遍歷被驅動表T2并判斷T2中是否還存在匹配的記錄,直到遍歷完驅動結果集1中所有的記錄為止。這里的外層循環是指遍歷驅動結果集1所對應的循環,內層循環是指遍歷被驅動表T2所對應的循環。顯然,外層循環所對應的驅動結果集1有多少條記錄,遍歷被驅動表T2的內層循環就要做多少次,這就是所謂的“嵌套循環”的含義。
嵌套循環連接的優缺點及適用場景總結如下:
從上述嵌套循環連接的具體執行過程可以看出:如果驅動表所對應的驅動結果集的記錄數較少,同時被驅動表的連接列上又存在唯一性索引(或者在被驅動表的連接列上存在選擇性很的的非唯一性索引),那么此時使用嵌套循環連接的執行效率就會非常高;但如果驅動表所對應的驅動結果集的記錄數很多,即便在被驅動表的連接列上存在索引,此時使用嵌套循環連接的執行效率也不會高。
只要驅動結果集的記錄數較少,那就具備了做嵌套循環連接的前提條件,而驅動結果集是在對驅動表應用了目標SQL中指定的謂詞條件(如果有的話)后所得到的結果集,所以大表也可以作為嵌套循環連接的驅動表,關鍵看目標SQL中指定的謂詞條件(如果有的話)能否將驅動結果集的數據量降下來。
嵌套循環連接有其他連接方法所沒有的一個優點:嵌套循環連接可以實現快速響應,即它可以第一時間返回已經連接過具滿足連接條件的記錄,而不必等待所有的連接操作全部做完才返回連接結果。雖然排序合并連接也可以,但它們并不是第一時間返回,因為排序合并連接要等到排序完后做合并操作時才能開始返回數據,而哈希連接則要等到驅動結果集所對應的Hash Table全部建完后才能開始返回數據。
如果Oracle使用的是嵌套循環連接,且在被驅動表的連接列上存在索引,那么Oracle在訪問索引時通常會使用單塊讀,這意味著嵌套循環連接的驅動結果集有多少條記錄,Oracle就會需要訪問該索引多少次。另外,如果目標SQL中的查詢列并不能全部從驅動表的相關索引中獲得,那么Oracle在做完嵌套循環連接后還需要對被驅動表執行回表操作。這個回青操作通常也會使用單塊讀,這意味著做完嵌套循環連接后的連接結果集有多少條記錄,Oracle就需要回表多少次。
為了提高嵌套循環連接的執行效率,在Oracle 11g中,Oracle引入了向量I/O(Vector I/O)。在引入向量I/O后,Oracle就可以將原先一批單塊讀所需要耗費的物理I/O結合起來,然后用一個向量I/O去批處理它們,這樣就實現了在單塊讀的數量不降低的情況下減少這些單塊讀所需要耗費的物理I/O數量,也就提高了嵌套循環連接的執行效率。
1.2.3 哈希連接
哈希連接(Hash Join)是一種兩個表在做表連接時主要依靠哈希運算來得到連接結果集的表連接方法。
在Oracle7.3之前,Oracle數據庫中常用的表連接方法就只有排序合并連接和嵌套循環連接這兩種,但這兩種方法都各有其明顯缺陷。對于排序合并連接,如果兩個表在施加了目標SQL中指定的謂詞條件(如果有的話)后得到的結果集很大且需要排序,則排序合并連接的執行效率一定不高;而對于嵌套循環連接,如果驅動表所對應的驅動結果集的記錄數很大,即便在被驅動表的連接列上存在索引,此時使用嵌套循環連接的執行效率也會同樣不高。為了上述情形下效率不高的問題,同時也為了給優化器提供一種新的選擇,Oracle在7.3中引入了哈希連接。從理論上來說,哈希連接的執行效率會比排序合并連接和嵌套循環連接要高,當然,實際情況并不總是這樣。
在Oracle 10g及其以后的Oracle數據庫版本中,優化器(實際上是CBO,因為哈希連接僅適用于CBO)在解析目標SQL時是否考慮哈希連接是受限于隱含參數_HASH_JOIN_ENABLED,而在Oracle10g以前,CBO在解析目標SQL時是否考慮哈希連接是受限于參數HASH_JOIN_ENABLED。_HASH_JOIN_ENABLED的默認值是TRUE,表示允許CBO在解析目標SQL時考慮哈希連接。當然,即使該參數為FALSE,使用USE_HASH Hint依然可以讓CBO在解析目標SQL時考慮哈希連接,這說明USE_HASH Hint的優先級比參數_HASH_JOIN_ENABLED的優先級要高。
如果兩個表(假如為T1和T2)在做表連接時使用的是哈希連接,則Oracle會依次順序執行如下步驟:
首先Oracle會根據參數HASH_AREAS_SIZE、DB_BLOCK_SIZE和_HASH_MULTIBLOCK_IO_COUNT的值來決定Hash Partition的數據(Hash Partition是一個邏輯上的概念,它實際上是一組Hash Bucket的集合。所有Hash Partition的集合就被稱為Hash Table,即一個Hash Table由多個Hash Partition所組成,而一個HashPartition又由多個Hash Bucket所組成的)。
表T1和T2在施加了目標SQL中指定的謂詞條件(如果有的話)后,得到的結果集中數量較少的那個結果集會被Oracle選為哈希連接的驅動結果集,這里我們假設T1所對應的結果集的數據量相對較少,記為S;T2所對應的結果集的數據相對較多,記為B。顯然這里S是驅動結果集,B是被驅動結果集。
接著Oracle會遍歷S,讀取S中的每一條記錄,并對每一條記錄按照該記錄在表T1中的連接列做哈希運算。這個哈希運算會使用兩個內置哈希函數,這兩個哈希函數會同時對該連接列計算哈希值,我們把這兩個內置哈希函數分別記為hash_func_1和hash_func_2,它們所計算出來的哈希值分別記為hash_vale_1和hash_value_2。
然后Oracle會按照hash_value_1的值把相應的S中的對應記錄存儲在不同的Hash Partition的不同Hash Bucket里,同時和該記錄存儲在一起的還有該記錄用hash_func_2計算出來的hash_value_2。注意,存儲在Hash Bucket里的記錄并不是目標表的完整行記錄,只需要存儲位置目標SQL中與目標表相關的查詢列和連接列就足夠了。我們把S所對應的每一個Hash Partition記為Si。
在構建Si的同時,Oracle會構建一個位圖(BITMAT),這個位置用來標記Si所包含的每一個Hash Bucket是否記錄(即記錄數是否大于0)。
如果S的數據量很大,那么在構建S所對應的Hash Table時,就可能會出現PGA的工作區(WORK AREA)被填滿的情況。這時候Oracle會把工作區中包含記錄數最多的Hash Partition寫到磁盤上(TEMP表空間)。接著Oracle會繼續構建S所對應的Hash Table,在繼續構建的過程中,如果工作區又滿了,則Oracle會繼續重復上述動作,即挑選包含記錄數最多的Hash Partition并寫回到磁盤上。如果要構建的記錄所對應的Hash Partition已經事先被Oracle寫回磁盤,則此時Oracle會去磁盤上更新Hash Partition ,即把該條記錄和Hash_vale_2直接加到這個已經位于磁盤上的Hash Partition的相應Hash Bucket中。注意,極端情況下可能會出現只有某個Hash Partition的部分記錄不覺 在內存中,該Hash Partition的剩余部分和余下的所有Hash Partition都已經被寫回到磁盤上。
上述構建S所對應的Hash Table的過程會一直持續下去,直到遍歷完S中的所有記錄為止。
接著,Oracle會對所有的Si按照它們所包含的記錄數來排序,然后把這些已經誹好序的Hash Partition按順序依次且盡可能全部放到內存中(PGA的工作區),當然,如果實在放不下,放不下的那部分Hash Partition還是會位于磁盤上。
至此Oracle已經處理完S,現在可以開始處理B了。
Oracle會遍歷B,讀取B中的每一條記錄,并按照該記錄在表T2中的連接列做哈希運算,這個哈希運算和步驟3中的哈希運算是一模一樣的,即還是會用步驟3中的hash_func_1和hash_func_2,并且也會計算出兩個哈希值hash_value_1和hash_value_2。
接著Oracle會按照該記錄所對應的哈希值hash_value_1去Si里找匹配的Hash Bucket中的每一條記錄的連接列,看是否是真的匹配(即這里要校驗S和B中匹配記錄所對應的連接列是否真的相等,因為對于哈希運算而言,不同的值經過哈希運算后的結果可能是相同的)。如果真的匹配,則上述hash_value_1所對應B中記錄的位于目標SQL中的查詢列和該Hash Bucket中的匹配記錄便會組合起來,一起作為滿足目標SQL連接條件的記錄返回。如果找不到匹配的Hash Bucket,則Oracle就會去訪問步驟5中構建的位圖。
如果位圖顯示該Hash Bucket在Si中對應的記錄數大于0,則說明該Hash Bucket雖然不在內存中,但它已經被寫回磁盤,此時Oracle就會按照hash_value_1的值把相應B中的對應記錄也可以Hahs Partition的方式寫回到磁盤上,同時和該記錄存儲在一起的還有該記錄用hash_func_2計算出來的hash_value_2的值。如果位圖顯示該Hash Bucket在Si中對應 的記錄數等于0,則Oralce就無須把上述hash_value_1所對應B中的記錄寫回磁盤了,因為這條記錄必須不滿足目標SQL的連接條件。這個根據位置來決定是否將hash_value_1所對就B的記錄寫回到磁盤的動作就是所謂的“位圖過濾”(Oralce不一定會啟用位圖過濾,因為如果所有的Si本來就都在內存中,也沒發生過將Si寫回到磁盤的操作,那么這里Oracle就不需要啟用位圖過濾了)。我們把B所對應的每一個Hash Partition記為Bj。
上述去Si中查找匹配Hash Bucket和構建Bj的過程會一直持續下去,直到遍歷完B中的所有記錄為止。
至此Oracle已經處理完所有位于內存中的Si和對應的Bj,現在只剩下位于磁盤上的si和Bj還未處理。
因為在構建Si和Bj時用的是同樣的哈希函數hash_func_1和hash_func_2,所以Oracle在處理位于磁盤上的Si和Bj的時候可以放心地配對處理,即只有對應Hash Partition Number值相同的Si和Bj才可能會產生滿足連接條件的記錄。這里我們用Sn和Bn來表示位于磁盤上且對應Hash Partition Number值相同的Si和Bj。
對于每一對Sn和Bn,它們之中記錄數較少的會被當作驅動結果集,然后Oracle會用這個驅動結果集Hash Bucket里的記錄的hash_vale_2來構建新的Hash Table,另外一個記錄數較多的會被當作被驅動結果集,然后Oracle會用這個被驅動結果集Hash Bucket里記錄的hash_value_2去上述構建的新的Hash Table中找匹配記錄。注意,對每一對Sn和Bn而言,Oracle始終會選擇它們中記錄數較少的來作為驅動結果集,所以每一對Sn和Bn的驅動結果集都可能會發生變化,這就是所謂的“動態角色互換”。
步驟14中如果存在匹配記錄,則該匹配記錄會作為滿足目標SQL連接條件的記錄返回。
上述處理Sn和Bn的過程會一直持續下去,直到遍歷完所有的Sn和Bn為止。
哈希連接的優缺點及適用場景總結如下:
哈希連接不一定會排序,或者說大多數情況下都不需要排序。
哈希連接的驅動表所對應的連接列的可能性應盡可能好,因為這個可選擇性會影響對應Hash Bucket中的記錄數,而Hash Bucket中的記錄數又會直接影響從該Hash Bucket中查找匹配記錄的效率。如果一個Hash Bucket里所包含的記錄數過多,則可能會嚴重降低所對應哈希連接的執行效率,此時典型的表現就是該哈希連接執行了很長時間都沒有結束,數據庫所在數據庫服務器上的CPU占用率很高,但目標SQL所消耗的邏輯讀卻很低,因為此時大部分時間都耗費在了遍歷上述Hash Bucket里的所有記錄上,而遍歷Hash Bucket里的記錄這個動作發生在PGA的工作區里,所以不耗費邏輯讀。
哈希連接只適用于CBO,它也只能用于等值連接條件(即使是哈希反連接,Oracle實際上也是將其轉換成了等價的等值連接)。
哈希連接很適合于小表和大表之間做表連接且連接結果集的記錄數較多的情形,特別是在小表的連接列的可選擇性非常好的情況下,哈希連接的執行時間就可以近似看作是和全表掃描那個大表所耗費的時間相當。
當兩個表做哈希連接時,如果在施加了目標SQL中指定的謂詞條件(如果有的話)后得到的數據量較小的那個結果集所對應的Hash Table能夠完全被容納在內存(PGA的工作區),則此時的哈希連接的執行效率會非常高。
1.2.4 笛卡兒連接
笛卡兒連接(Cross Join)又稱為笛卡兒乘積(Caresian Product),這是一種兩個表在做表連接時沒有任何連接條件的表連接方法。
如果兩個表(假如為T1和T2)在做表連接時使用的是笛卡兒連接,則Oracle會依次順序執行如下步驟:
首先以目標SQL中指定的謂詞條件(如果有的話)訪問表T1,此時得到的結果集我們記為結果集1,這里假設結果集1的記錄數為m。
接著以目標SQL中指定的謂詞條件(如果有的話)訪問表T2,此時得到的結果集我們記為結果集2,這里假設結果集2的記錄數為n。
最后對結果集1和結果集2執行合并操作,從中取出匹配記錄來作為笛卡兒連接的最終執行結果。這里的特殊之處在于對于笛卡兒連接而言,因為淌有表連接條件,所以在對結果集1和結果集2執行合并操作時,對于結果集1中的任意一條記錄,結果集2中的所有記錄都滿足條件,即它們都會是匹配記錄,所以上述笛卡兒連接的連接結果的記錄數就是m和n的乘積(即m×n)。
語句示例:select t1.col1,t2.col3 from t1,t2;
標準SQL用關鍵字“CROSS JOIN”來表示笛卡兒連接,如select t1.col1,t2.col3 from t1 cross join t2;
對于笛卡兒連接的優缺點及適用場景總結如下:
笛卡兒連接的出現通常是由于目標SQL中漏寫了表連接條件,所以笛卡兒連接一般是不好的,除非刻意這樣做(比如有些情況下可以利用笛卡兒連接來減少對目標SQL中大表的全表掃描次數)。
有時候出現笛卡兒連接是因為目標SQL中使用了ORDERED Hint,同時在該SQL的SQL文本中位置相鄰的兩個表之間又沒有直接的關聯條件。
有時候出現笛卡兒連接是因為目標SQL中相關表的統計信息不準。比如三個表T1、T2和T3做表連接,T1和T2的連接條件為T1.ID1=T2.ID1,T2和T3的連接條件為T2.ID2=T3.ID2,同時在表T2的連接列ID1和ID2上存在一個包含這兩個連接列的組合索引。如果表T1和T3的統計信息不準,導致Oracle認為表T1和T3都只有很少量的記錄(比如都只有1條記錄),則此時Oracle很可能會選擇先對表T1和T3做笛卡兒連接,然后再和表T2做表連接。因為Oracle認為表T1和T3做笛卡兒連接后連接結果集的Cardinality的值是1,并且連接結果中間會同時包含列ID1和列ID2,這意味著此時Oracle就可以利用表T2中的上述組合索引了。這種笛卡兒連接通常是有問題的,如果表T1和T3的實際記錄數并不都是1,而全部是1000,那么此時表T1和表T3做笛卡兒連接的結果集的Cardinality的值將是100萬,顯然這種情況下如果還是按照笛卡兒連接的方式來執行的話,則該SQL的執行效率就會受到嚴重影響。
參考《基于Oracle的SQL優化》
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。