關於查詢轉換的一些簡單分析(一)
在sql解析器中,在生成執行計劃的時候,會在多個執行計劃中選擇最優的計劃,在這個過程中,查詢轉換就是一個很重要的過程。
雖然最終的執行結果沒有變化,但是從最佳化器的角度來看,查詢轉換的結果會更好。
大體來說查詢轉換有以下幾種型別。
檢視合併
子查詢解巢狀
謂詞推進
物化檢視查詢重寫
比如
select *from emp where deptno in (select deptno from dept)
會在查詢轉換中轉換為下面的樣子.
select e.* from emp e,dept d where e.deptno=d.deptno;
從查詢轉換的角度來說,兩者的查詢效果是基本一致的。
--檢視合併
select *
from emp e,
(select deptno from emp) s_view
where e.deptno=s_view.deptno;
70 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 3638257876
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 71 | 2911 | 5 (20)| 00:00:01 |
|* 1 | HASH JOIN | | 71 | 2911 | 5 (20)| 00:00:01 |
| 2 | TABLE ACCESS FULL| EMP | 14 | 532 | 2 (0)| 00:00:01 |
| 3 | TABLE ACCESS FULL| EMP | 14 | 42 | 2 (0)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("E"."DEPTNO"="DEPTNO")
來比較一下是否檢視合併的結果。
select
*
from emp e,
(select /*+no_merge*/ deptno from emp) s_view
where e.deptno=s_view.deptno
70 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 2243607326
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 71 | 3621 | 5 (20)| 00:00:01 |
|* 1 | HASH JOIN | | 71 | 3621 | 5 (20)| 00:00:01 |
| 2 | TABLE ACCESS FULL | EMP | 14 | 532 | 2 (0)| 00:00:01 |
| 3 | VIEW | | 14 | 182 | 2 (0)| 00:00:01 |
| 4 | TABLE ACCESS FULL| EMP | 14 | 42 | 2 (0)| 00:00:01 |
----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("E"."DEPTNO"="S_VIEW"."DEPTNO")
可以比較上面兩個查詢的時候,會發現第二個執行計劃的VIEW關鍵字表示,執行計劃是保持原樣的,會單獨處理檢視。
使用圓括號把子查詢括起來不一定在sql執行的時候會保證子查詢塊會單獨執行。
這個時候可以根據要求來選擇是否選擇檢視合併。
檢視合併在大體來說效能還是不錯的,不過在使用order by, 聚合函式,分析函式,group等等的時候,檢視合併的功能還是會受到限制或者無法啟用。
而且在試圖合併的功能受到限制的時候,如果硬要啟用檢視合併功能,可能查詢的結果會有一些變化,這個時候需要你來判斷是否確定要啟用檢視合併。
select e.ename,e.sal,v.avg_salary
from emp e,
(select deptno,avg(sal) avg_salary
from emp oe
group by deptno)v
where e.deptno=v.deptno and e.sal>1000;
12 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 269884559
-----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 14 | 546 | 6 (34)| 00:00:01 |
|* 1 | HASH JOIN | | 14 | 546 | 6 (34)| 00:00:01 |
| 2 | VIEW | | 3 | 78 | 3 (34)| 00:00:01 |
| 3 | HASH GROUP BY | | 3 | 21 | 3 (34)| 00:00:01 |
| 4 | TABLE ACCESS FULL| EMP | 14 | 98 | 2 (0)| 00:00:01 |
|* 5 | TABLE ACCESS FULL | EMP | 13 | 169 | 2 (0)| 00:00:01 |
-----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("E"."DEPTNO"="V"."DEPTNO")
5 - filter("E"."SAL">1000)
select /*+ merge(v)*/ e.ename,e.sal,v.avg_salary
from emp e,
(select deptno,avg(sal) avg_salary
from emp oe
group by deptno)v
where e.deptno=v.deptno and e.sal>1000;
12 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 4115741206
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 68 | 2176 | 6 (34)| 00:00:01 |
| 1 | HASH GROUP BY | | 68 | 2176 | 6 (34)| 00:00:01 |
|* 2 | HASH JOIN | | 68 | 2176 | 5 (20)| 00:00:01 |
|* 3 | TABLE ACCESS FULL| EMP | 13 | 325 | 2 (0)| 00:00:01 |
| 4 | TABLE ACCESS FULL| EMP | 14 | 98 | 2 (0)| 00:00:01 |
----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("E"."DEPTNO"="DEPTNO")
3 - filter("E"."SAL">1000)
--子查詢解巢狀
子查詢巢狀和檢視合併的不同之處在於檢視合併是在select xxx from的部分,而子查詢巢狀式在where xxxx的部分。
select *from emp where deptno in (select deptno from dept)
14 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 3956160932
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 14 | 532 | 2 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| EMP | 14 | 532 | 2 (0)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("DEPTNO" IS NOT NULL)
先來比較一下是否啟用子查詢巢狀的區別 ,如果不需要啟用子查詢巢狀,可以使用Hint no_unnest來引導。
select *from emp where deptno in (select /*+ no_unnest*/deptno from dept)
14 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 1783302997
------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 5 | 190 | 2 (0)| 00:00:01 |
|* 1 | FILTER | | | | | |
| 2 | TABLE ACCESS FULL| EMP | 14 | 532 | 2 (0)| 00:00:01 |
|* 3 | INDEX UNIQUE SCAN| PK_DEPT | 1 | 3 | 0 (0)| 00:00:01 |
------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter( EXISTS (SELECT /*+ NO_UNNEST */ 0 FROM "DEPT" "DEPT"
WHERE "DEPTNO"=:B1))
3 - access("DEPTNO"=:B1)
可以從謂語部分看出來,兩個的執行計劃還是有很大的不同,第二個執行計劃是未啟用子查詢解巢狀的形式。會在子查詢中走索引,然後透過exists子句來和外部查詢的deptno欄位連線。
而第一個執行計劃直接走了一個全表掃描,對於deptno的部分直接使用條件deptno is not null來過濾了。
select *from emp outer
where outer.sal>(select avg(inner.sal) from emp inner where inner.deptno=outer.deptno)
6 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 1245077725
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 64 | 6 (34)| 00:00:01 |
|* 1 | HASH JOIN | | 1 | 64 | 6 (34)| 00:00:01 |
| 2 | VIEW | VW_SQ_1 | 3 | 78 | 3 (34)| 00:00:01 |
| 3 | HASH GROUP BY | | 3 | 21 | 3 (34)| 00:00:01 |
| 4 | TABLE ACCESS FULL| EMP | 14 | 98 | 2 (0)| 00:00:01 |
| 5 | TABLE ACCESS FULL | EMP | 14 | 532 | 2 (0)| 00:00:01 |
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("ITEM_1"="OUTER"."DEPTNO")
filter("OUTER"."SAL">"AVG(INNER.SAL)")
這個例子中子查詢轉換為了一個內嵌式圖,然後與外部的查詢合併連線,相關列成為了連線條件,子查詢的剩餘部分成為了內嵌檢視。
sql的執行方式和下面的sql類似。
select *from emp outer,
(select inner.deptno,avg(inner.sal) avg_sal from emp inner group by inner.deptno)inner where inner.deptno=outer.deptno
and outer.sal>inner.avg_sal;
6 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 269884559
-----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 64 | 6 (34)| 00:00:01 |
|* 1 | HASH JOIN | | 1 | 64 | 6 (34)| 00:00:01 |
| 2 | VIEW | | 3 | 78 | 3 (34)| 00:00:01 |
| 3 | HASH GROUP BY | | 3 | 21 | 3 (34)| 00:00:01 |
| 4 | TABLE ACCESS FULL| EMP | 14 | 98 | 2 (0)| 00:00:01 |
| 5 | TABLE ACCESS FULL | EMP | 14 | 532 | 2 (0)| 00:00:01 |
-----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("INNER"."DEPTNO"="OUTER"."DEPTNO")
filter("OUTER"."SAL">"INNER"."AVG_SAL")
關於謂詞推進和物化檢視的部分,稍後補充。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/8494287/viewspace-1346980/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 關於查詢最佳化的一些總結
- Laravel 關聯查詢 ——一對一 簡單例子Laravel單例
- 對於專案中簡單的多條件查詢的一些心得體會
- 關於 Elasticsearch nested field /script 的一些複雜查詢Elasticsearch
- 簡單的查詢
- Laravel 5 關聯查詢 ——一對 N 簡單例子Laravel單例
- vim下多行查詢替換簡單命令
- Oracle 查詢轉換Oracle
- 關於SQL資料庫一些簡單的筆記SQL資料庫筆記
- 對 MySQL 慢查詢日誌的簡單分析MySql
- openGauss核心分析2:簡單查詢的執行
- MySQL - 資料查詢 - 簡單查詢MySql
- 關於MySQL 通用查詢日誌和慢查詢日誌分析MySql
- Mybatis簡單查詢MyBatis
- 微信小程式關於分享一些簡單總結微信小程式
- 記一次詭異的Oracle查詢轉換Oracle
- 簡單的查詢語法
- 關於檔案讀寫使用RandomAccessFile類的一些簡單操作randomMac
- 【恩墨學院】一次由查詢轉換引起的效能問題的分析
- Oracle 查詢轉換-01 or expansionOracle
- 關於單元測試的一些想法
- 【redis】關於查詢和分析redis中的bigkeys問題Redis
- MySQL的簡單查詢語句MySql
- 插值查詢的簡單理解
- Laravel 5 關聯查詢 —— N 對 N 簡單例子Laravel單例
- Oracle 查詢轉換-02 View MergingOracleView
- Oracle 查詢轉換-03 Predicate PushingOracle
- Oracle 查詢轉換-04 Subquery UnnestingOracle
- 【SQL】Oracle查詢轉換之 OR用法SQLOracle
- Oracle 12CR2查詢轉換之星型轉換Oracle
- 關於oracle的空間查詢Oracle
- SpringBoot 框架MongoDB 簡單的查詢方式Spring Boot框架MongoDB
- 二分查詢的簡單理解
- SSM的查詢簡單練習+JSPSSMJS
- 關於《龍族幻想》的系統以及簡單分析
- 【SQL】Oracle查詢轉換之物化檢視查詢重寫SQLOracle
- ATM-簡單SQL查詢SQL
- 關於SSM框架的一個簡單DemoSSM框架
- iOS關於換膚和夜間模式的一些思考iOS模式