Oracle Hint no_merge(merge)、no_unnest(unnest)的作用對象陷阱
Oracle的查詢轉換中有兩個概念,子查詢展開和視圖合並。
關於兩者的概念這里不贅述,可以看看崔華的《基於Oracle的SQL優化》這本書。
其中,no_unnest(unnest)這個Hint顯式控制是否對子查詢做展開,no_unnest表示該子查詢不展開,unnest表示子查詢展開。
而no_merge(merge)這個Hint顯式控制是否進行視圖合並,no_merge表示視圖不合並,merge表示試圖合並。
實際上最近又遇到對子查詢使用no_unnest不生效的情況了,然后使用no_merge生效的情況。
還楞了好一會才反應過來,所以決定還是記錄下。
崔華的《基於Oracle的SQL優化》的P642頁介紹了merge和no_merge的使用對象和作用,
MERGE是針對單個目標視圖的Hint,它的含義是讓優化器對目標視圖執行視圖合並(View Merging)。
而P643頁介紹了unnest和no_unnest的使用對象和作用,
UNNEST是針對子查詢的Hint,它的含義是讓優化器對目標SQL中的子查詢執行子查詢展開(Subquery Unnesting)。
舉個例子,select * from a left join (select * from m where m.id=100) b on a.id=b.id。
這條sql使用no_unnest是不生效的,比如這就是有問題的:select * from a left join (select /*+ no_unnest */ * from m where m.id=100) b on a.id=b.id。
只能使用no_merge。
搞清楚這個問題只需要理解子查詢的概念:當一個查詢是另一個查詢的條件時,稱之為子查詢。
一直潛意識就認為子查詢就是嵌套的另外一些SELECT查詢,實際上還要滿足是另外一個查詢的條件。
像上邊的例子,顯然不滿足第二點。
而且實際上可以拿一個視圖名字替換掉查詢塊內容而不會產生語法錯誤,比如變成select * from a left join view_name b on a.id=b.id那么這里就起到的是”視圖“的作用,因此用no_merge。
像select * from a where a.id in (select b.id from b)則無法替換掉,否則產生語法錯誤。比如select * from a where a.id in view_name那就有問題了。
最后引用崔華的《基於Oracle的SQL優化》P337的一段話:
Oracle數據庫里子查詢前的where條件如果是如下這些條件之一,那么這種類型的目標SQL在滿足了一定的條件后就可以做子查詢展開:
- SINGLE-ROW(即=、<、>、<=、>=和<>)
- EXISTS
- NOT EXISTS
- IN
- NOT IN
- ANY
- ALL