執行計劃-6:推入子查詢
在這個系列的第5部分,我們看了一些需要額外注意的”第一個子操作先執行”的例子。在第6部分中,我們將繼續探討一個原則—“子查詢推入”,其中“第一個子操作先執行”可能會導致錯誤的結論。
Access or Filter
下面這兩個執行計劃的基本形狀是很相似的,我們很容易假設,我們應該把這兩種情況下的操作順序基本上解釋為“從下到上”。
-------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 19 | 2 | | 1 | TABLE ACCESS BY INDEX ROWID | MIN_MAX | 1 | 19 | 2 | |* 2 | INDEX UNIQUE SCAN | MM_PK | 1 | | 1 | | 3 | SORT AGGREGATE | | 1 | 11 | | |* 4 | TABLE ACCESS BY INDEX ROWID| MIN_MAX | 1 | 11 | 3 | |* 5 | INDEX RANGE SCAN | MM_PK | 10 | | 2 | -------------------------------------------------------------------------- ------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost | ------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 19 | 5 | | 1 | TABLE ACCESS BY INDEX ROWID | MIN_MAX | 1 | 19 | 2 | |* 2 | INDEX UNIQUE SCAN | MM_PK | 1 | | 1 | |* 3 | TABLE ACCESS BY INDEX ROWID| MIN_MAX | 1 | 11 | 3 | |* 4 | INDEX RANGE SCAN | MM_PK | 2 | | 2 | -------------------------------------------------------------------------
實際上”第一個子操作先執行”對於第一個計劃來說是正確的,它會從下到上執行,但是對於第二個計劃來說是行不通的,由於優化器呈現的是“推入”過的篩選子查詢,所以計劃的形狀被扭曲了,也就是說,在儘可能早的時間執行。 很難從計劃的主體中看出這是否已經發生了,你真的需要檢查計劃的謂詞部分,甚至可以參考原始語句來理解發生了什麼。
這裡按順序展示了上面兩個執行計劃的謂詞部分:
Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("MM1"."ID_PARENT"=100 AND "MM1"."ID_CHILD"= (SELECT MAX("MM2"."ID_CHILD") FROM "MIN_MAX" "MM2" WHERE "MM2"."ID_PARENT"=100 AND "STATUS"=1)) 4 - filter("STATUS"=1) 5 - access("MM2"."ID_PARENT"=100) Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("MM1"."ID_PARENT"=100 AND "MM1"."ID_CHILD"=1) filter( EXISTS (SELECT /*+ PUSH_SUBQ NO_UNNEST */ 0 FROM "MIN_MAX" "MM2" WHERE "MM2"."ID_PARENT"=100 AND "STATUS"=1 AND ("MM2"."ID_CHILD"=TRUNC(:B1) OR "MM2"."ID_CHILD"=TRUNC(:B2+1)))) 3 - filter("STATUS"=1) 4 - access("MM2"."ID_PARENT"=100) filter("MM2"."ID_CHILD"=TRUNC(:B1) OR "MM2"."ID_CHILD"=TRUNC(:B2+1))
注意第2行出現的關鍵差異——在兩個執行計劃中的操作都是索引唯一掃描。 第一個計劃只報告一個訪問謂詞(包含一個子查詢),而第二個計劃同時顯示一個訪問謂詞(它由針對索引列的簡單謂片語成)和一個篩選謂詞(它由一個子查詢組成)。
對於一個索引的操作,訪問謂詞告訴我們範圍掃描的起始和結束的值,過濾謂詞告訴我們需要怎麼針對每一個在範圍內的索引條目的進行驗證。看過了謂詞部分的不同之後我們可以解釋計劃:
First plan:
第5行我們對mm_pk索引進行範圍掃描,第4行訪問min_max表的某些行,在第3行聚合這些行並且找到id_child列的最大值,之後在第2行我們使用這個值對mm_pk進行索引唯一掃描,最後第1行中我們訪問表min_max並找到我們所需要的這一行。操作的順序為:5,4,3,2,1,0.
Second plan:
第2行我們使用提供的謂詞對mm_pk進行索引唯一掃描,之後在第4行中使用第一個索引掃描返回的值進行範圍掃描,再之後在第3行中訪問min_max表查詢是否有符合條件的行,如果有就在第1行返回最多一行的rowid。操作的順序為:2,4,3,1,0。
下面展示原始查詢語句可能更易於理解執行的過程。這些語句只是為了說明這小節表達的觀點,所以並不需要理解語句為什麼要這麼寫。
select small_vc from min_max mm1 where mm1.id_parent = 100 and mm1.id_child = ( select max(mm2.id_child) from min_max mm2 where mm2.id_parent = 100 and status = 1 ) ; select small_vc from min_max mm1 where mm1.id_parent = 100 and mm1.id_child = 1 and exists ( select /*+ no_unnest push_subq */ null from min_max mm2 where mm2.id_parent = 100 and ( mm2.id_child = trunc(mm1.id_child) or mm2.id_child = trunc(mm1.id_child + 1) ) and status = 1 ) ;
有了前面的兩個查詢語句,就更容易看到第一個可以通過先執行子查詢然後使用結果來驅動主查詢;第二個查詢必須先執行主查詢,然後每個步驟都需要停止並檢測子查詢的存在性。
你會注意到我在第二個查詢優化器使用hint,阻止優化器做任何轉換。我使用了no_unnest塊來阻止子查詢展開,然後我告訴優化器儘可能早的執行子查詢;後一種hint影響了執行計劃的形狀(即操作的順序)。如果我把hint改為no_push_subq(阻止子查詢推入),計劃就會變為:
------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost | ------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 1 | 19 | 5 | |* 1 | FILTER | | | | | | 2 | TABLE ACCESS BY INDEX ROWID| MIN_MAX | 1 | 19 | 2 | |* 3 | INDEX UNIQUE SCAN | MM_PK | 1 | | 1 | |* 4 | TABLE ACCESS BY INDEX ROWID| MIN_MAX | 1 | 11 | 3 | |* 5 | INDEX RANGE SCAN | MM_PK | 2 | | 2 | ------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter( EXISTS (SELECT /*+ NO_PUSH_SUBQ NO_UNNEST */ 0 FROM "MIN_MAX" "MM2" WHERE "MM2"."ID_PARENT"=100 AND "STATUS"=1 AND ("MM2"."ID_CHILD"=TRUNC(:B1) OR "MM2"."ID_CHILD"=TRUNC(:B2+1)))) 3 - access("MM1"."ID_PARENT"=100 AND "MM1"."ID_CHILD"=1) 4 - filter("STATUS"=1) 5 - access("MM2"."ID_PARENT"=100) filter("MM2"."ID_CHILD"=TRUNC(:B1) OR "MM2"."ID_CHILD"=TRUNC(:B2+1))
有了這個更改,“第一個子操作先執行”規則又可以工作了:第1行的過濾操作呼叫第2行來返回行,然後呼叫第4行來決定是否保留該行。我們可以通過檢查謂詞部分來確認這種解釋,在該部分中,我們可以看到子查詢的文字現在作為過濾器謂詞出現在第1行。
通過使用' outline '選項作為對dbms_xplan呼叫的格式化引數,您可以在檢查已推入的子查詢方面獲得一些幫助,因為這將允許您檢視大綱/SQL計劃基線中是否有與計劃相關的push_subq()提示。
這個例子向您展示了推入子查詢的潛在好處——當我們阻塞推入時,Oracle直到它訪問了表並獲得了我們不需要的行之後才執行子查詢;另一方面,當我們推入子查詢時,Oracle會在索引項可用時立即執行它,從而避免了冗餘表訪問。
Conclusion
在所有這些計劃與“第一個子操作先執行”不匹配的例子中,我們已經看到了推入的子查詢——在某些情況下,我不得不在SQL中加入一個顯式的hint來建立我想要演示的計劃型別。當你試圖為包含多個子查詢的語句解釋執行計劃時,請注意推入的子查詢可能會導致“正常”形狀的失真。你可以仔細通過謂詞部分檢查,能否看到一些子查詢文字作為一個過濾謂詞出現。
原文連結:
https://www.red-gate.com/simple-talk/sql/oracle/execution-plans-part-6-pushed-subqueries/
原文作者:
Jonathan Lewis
| 譯者簡介
林錦森·沃趣科技資料庫技術專家
沃趣科技資料庫工程師,多年從事Oracle資料庫,較豐富的故障處理、效能調優、資料遷移及備份恢復經驗。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/28218939/viewspace-2667904/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- explain 查詢執行計劃AI
- 在MySQL中使用explain查詢SQL的執行計劃MySqlAI
- [oracle] 查詢歷史會話、歷史執行計劃Oracle會話
- PostgreSQL 查詢當前執行中sql的執行計劃——pg_show_plans模組SQL
- TiDB 查詢優化及調優系列(四)查詢執行計劃的調整及優化原理TiDB優化
- 執行計劃-1:獲取執行計劃
- 多執行緒查詢執行緒
- 分析執行計劃優化SQLORACLE的執行計劃(轉)優化SQLOracle
- MySQL執行計劃MySql
- SYBASE執行計劃
- MySQL 執行計劃MySql
- 執行計劃-5:第一個子操作的變化
- 複雜查詢—子查詢
- 【執行計劃】Oracle獲取執行計劃的幾種方法Oracle
- SQL查詢的:子查詢和多表查詢SQL
- 9i and 10g 透過SQL_ADDRESS 或sql_id查詢執行計劃SQL
- MySQL查詢優化之優化器工作流程以及優化的執行計劃生成MySql優化
- MySQL執行計劃解析MySql
- mysql explain 執行計劃MySqlAI
- explain執行計劃分析AI
- mysql執行計劃explainMySqlAI
- oracle 固定執行計劃Oracle
- Oracle sql執行計劃OracleSQL
- 執行計劃執行步驟原則
- sql查詢是如何執行的?SQL
- 多執行緒查詢,效率翻倍執行緒
- MYsql 子查詢MySql
- MySQL子查詢MySql
- 子串查詢
- 【PG執行計劃】Postgresql資料庫執行計劃統計資訊簡述SQL資料庫
- union的兩個子查詢是否並行並行
- MySQL 查詢常用操作(0) —— 查詢語句的執行順序MySql
- 自適應查詢執行:在執行時提升Spark SQL執行效能SparkSQL
- mysql-分組查詢-子查詢-連線查詢-組合查詢MySql
- mongodb執行計劃解釋MongoDB
- 檢視 OceanBase 執行計劃
- MySQL執行計劃解析(四)MySql
- 讀懂MySQL執行計劃MySql