執行計划是什么呢?比如你執行一條sql語句,查詢優化器會為這條sql語句設計執行方式,交給執行器去執行,查詢優化器設計的執行方式就是執行計划。
EXPLAIN可以打印出語句的執行計划。
那么,執行計划主要是由什么組成的呢?答案是操作符(個人理解)。
執行計划是由各類操作符組成的一顆樹,從內到外依次執行,縮進越多的越先執行,同樣縮進的上面的先執行,下面的后執行,上下的優先級高於內外。
達夢執行計划涉及到的一些主要操作符有:
CSCN :基礎全表掃描(a),從頭到尾,全部掃描
SSCN :二級索引掃描(b), 從頭到尾,全部掃描
SSEK :二級索引范圍掃描(b) ,通過鍵值精准定位到范圍或者單值
CSEK :聚簇索引范圍掃描© ,通過鍵值精准定位到范圍或者單值
BLKUP :根據二級索引的ROWID 回原表中取出全部數據(b + a)
一、執行計划解讀
SQL> explain select * from SYSOBJECTS;
1 #NSET2: [0, 1531, 396]
2 #PRJT2: [0, 1531, 396]; exp_num(17), is_atom(FALSE)
3 #CSCN2: [0, 1531, 396]; SYSINDEXSYSOBJECTS(SYSOBJECTS as SYSOBJECTS)
從上面的執行計划中我們可以看到哪些信息呢?
首先,一個執行計划由若干個計划節點組成,如上面的1、2、3。
然后我們看到,每個計划節點中包含操作符(CSCN2)和它的代價([0, 1711, 396])等信息。
代價由一個三元組組成[代價,記錄行數,字節數]。
代價的單位是毫秒,記錄行數表示該計划節點輸出的行數,字節數表示該計划節點輸出的字節數。
拿上面第三個計划節點舉例:操作符是CSCN2即全表掃描,代價估算是0ms,掃描的記錄行數是1711行,輸出字節數是396個。
二、舉例說明操作符
1、准備測試表和數據
DROP TABLE T1; DROP TABLE T2; CREATE TABLE T1(C1 INT ,C2 CHAR(1),C3 VARCHAR(10) ,C4 VARCHAR(10) ); CREATE TABLE T2(C1 INT ,C2 CHAR(1),C3 VARCHAR(10) ,C4 VARCHAR(10) ); INSERT INTO T1 SELECT LEVEL C1,CHR(65+MOD(LEVEL,57)) C2,'TEST',NULL FROM DUAL CONNECT BY LEVEL<=10000; INSERT INTO T2 SELECT LEVEL C1,CHR(65+MOD(LEVEL,57)) C2,'TEST',NULL FROM DUAL CONNECT BY LEVEL<=10000; CREATE INDEX IDX_C1_T1 ON T1(C1); SP_INDEX_STAT_INIT(USER,'IDX_C1_T1');-- 收集指定的索引的統計信息
這里說明一下SP_INDEX_STAT_INIT的兩個參數分別是模式名和索引名。我這里指定的是USER,會默認查找當前登錄用戶同名的模式,如果這個用戶下有多個模式,查不到其他模式。
2、NSET:收集結果集
說明:用於結果集收集的操作符, 一般是查詢計划的頂層節點。
EXPLAIN SELECT * FROM T1;

3、PRJT:投影
說明:關系的“投影”(project)運算,用於選擇表達式項的計算;廣泛用於查詢,排序,函數索引創建等。
EXPLAIN SELECT * FROM T1;

4、SLCT:選擇
說明:關系的“選擇” 運算,用於查詢條件的過濾。
EXPLAIN SELECT * FROM T1 WHERE C2='TEST';

5、AAGR:簡單聚集
說明:用於沒有group by的count,sum,age,max,min等聚集函數的計算。
EXPLAIN SELECT COUNT(*) FROM T1 WHERE C1 = 10;

6、FAGR:快速聚集
說明:用於沒有過濾條件時從表或索引快速獲取 MAX/MIN/COUNT值,DM數據庫是世界上單表不帶過濾條件下取COUNT值最快的數據庫。
EXPLAIN SELECT COUNT(*) FROM T1;

7、HAGR:HASH分組聚集
說明:用於分組列沒有索引只能走全表掃描的分組聚集,C2列沒有創建索引。
EXPLAIN SELECT COUNT(*) FROM T1 GROUP BY C2;

8、SAGR:流分組聚集
說明:用於分組列是有序的情況下可以使用流分組聚集,C1上已經創建了索引,SAGR2性能優於HAGR2。
EXPLAIN SELECT COUNT(*) FROM T1 GROUP BY C1;

官方解釋是:如果輸入流是有序的,則使用流分組,並計算聚集函數。
9、BLKUP:二次掃描
說明:先使用2級別索引定位,再根據表的主鍵、聚集索引、 rowid等信息定位數據行。
EXPLAIN SELECT * FROM T1 WHERE C1=10;

bookmark lookup 翻譯成中文是書簽查找
BLKUP2 官方文檔說明是:定位查找
10、CSCN:全表掃描
說明:CSCN2是CLUSTER INDEX SCAN的縮寫即通過聚集索引掃描全表,全表掃描是最簡單的查詢,如果沒有選擇謂詞,或者沒有索引可以利用,則系統一般只能做全表掃描。在一個高並發的系統中應盡量避免全表掃描。
EXPLAIN SELECT * FROM T1;

11、SSEK、CSEK、SSCN:索引掃描
1) SSEK
說明:SSEK2是二級索引掃描即先掃描索引,再通過主鍵、聚集索引、ROWID等信息去掃描表;
EXPLAIN SELECT * FROM T1 WHERE C1=10;

2)CSEK
說明:CSEK2是聚集索引掃描只需要掃描索引,不需要掃描表;
CREATE CLUSTER INDEX IDX_C1_T2 ON T2(C1);
EXPLAIN SELECT * FROM T2 WHERE C1=10;

3)SSCN
說明:SSCN是索引全掃描,不需要掃描表。
官方解釋是:直接使用二級索引進行掃描。
CREATE INDEX IDX_C1_C2_T1 ON T1(C1,C2);
EXPLAIN SELECT C1,C2 FROM T1;

三、簡單連接查詢例子
CREATE TABLE T1(C1 INT,C2 CHAR);
CREATE TABLE T2(D1 INT,D2 CHAR);
CREATE INDEX IDX_T1_C1 ON T1(C1);
INSERT INTO T1 VALUES(1,'A');
INSERT INTO T1 VALUES(2,'B');
INSERT INTO T1 VALUES(3,'C');
INSERT INTO T1 VALUES(4,'D');
INSERT INTO T2 VALUES(1,'A');
INSERT INTO T2 VALUES(2,'B');
INSERT INTO T2 VALUES(5,'C');
INSERT INTO T2 VALUES(6,'D');
SELECT A.C1+1,B.D2 FROM T1 A, T2 B WHERE A.C1 = B.D1;
EXPLAIN SELECT A.C1+1,B.D2 FROM T1 A, T2 B WHERE A.C1 = B.D1;

用到的操作符說明:
NSET2 結果集(result set)收集,一般是查詢計划的頂層節點
PRJT2 關系的―投影‖(project)運算,用於選擇表達式項的計算
NEST LOOP INDEX JOIN2 索引內連接
CSCN2 聚集索引掃描
SSEK2 二級索引數據定位
該計划的大致執行流程如下:
1) CSCN2: 掃描 T2 表的聚集索引,數據傳遞給父節點索引連接;
2) NEST LOOP INDEX JOIN2: 當左孩子有數據返回時取右側數據;
3) SSEK2: 利用 T2 表當前的 D1 值作為二級索引 IDX_T1_C1 定位查找的 KEY,返回結果給父節點;
4) NEST LOOP INDEX JOIN2: 如果右孩子有數據則將結果傳遞給父節點 PRJT2,否則繼續取左孩子的下一條記錄;
5) PRJT2: 進行表達式計算 C1+1, D2;
6) NSET2: 輸出最后結果;
7) 重復過程 1) ~ 4)直至左側 CSCN2 數據全部取完。
應該是這么個意思,取右側的一個值,去左側匹配,然后到PRJT2去計算,接着再去取右側一個值,再去左側匹配,如此循環,直到匹配完,不知道我理解的對不對。
四、單表
CREATE TABLE T1(C1 INT,C2 INT);
insert into t1 select level,level from dual connect by level < 10000;
1、全表掃描(無索引時)
explain select * from t1 where c1 = 5;

用到的操作符說明:
NSET2 結果集(result set)收集,一般是查詢計划的頂層節點
PRJT2 關系的―投影‖(project)運算,用於選擇表達式項的計算
SLCT2 關系的―選擇‖(select)運算,用於查詢條件的過濾
CSCN2 聚集索引掃描
說明:創建了一個普通表,沒有任何索引,過濾,從T1中取出數據只能走全表掃描CSCN
2、t1(c1)加索引i_test1
create index i_test1 on t1(c1);
1)直接使用二級索引掃描
explain select c1 from t1;

用到的操作符說明:
NSET2 結果集(result set)收集,一般是查詢計划的頂層節點
PRJT2 關系的―投影‖(project)運算,用於選擇表達式項的計算
SSCN 直接使用二級索引進行掃描
說明:這個時候T1存在兩個入口,CSCN T1基表(全表掃描T1),或者SSCN 二級索引I_TEST1,本例只要求獲取C1,二級索引上存在C1,且數據長度比基礎表要少(基表多出一個C2),索引選擇SSCN。
2)全表掃描(有索引時)
explain select c2 from t1;

用到的操作符說明:
NSET2 結果集(result set)收集,一般是查詢計划的頂層節點
PRJT2 關系的―投影‖(project)運算,用於選擇表達式項的計算
CSCN2 聚集索引掃描
說明:依然沒有更好的入口,還是選擇CSCN全表
3)定位查找
explain select * from t1 where c1 = 5;

用到的操作符說明:
NSET2 結果集(result set)收集,一般是查詢計划的頂層節點
PRJT2 關系的―投影‖(project)運算,用於選擇表達式項的計算
BLKUP2 定位查找
SSEK2 二級索引數據定位
說明:查詢條件C1 = 多少,存在C1索引,需要注意的是操作符后面的描述scan_range[5,5],表示精准定位到5,無疑,多數情況下這樣是比較有效率的。
另外一點,SSEK 上面出現了BLKUP操作符,由於I_TEST1上沒有C2的數據,而查詢需要SELECT *,索引需要BLKUP回原表查找整行數據。
很容易的,我們可以想到如果只查詢C1,那么BLKUP操作符應該不存在,驗證一下。
explain select c1 from t1 where c1 = 5;

3、聚簇索引
1)ROWID聚簇索引
聚簇索引是比較特殊的索引(對應操作符CSEK),在DM7上,同一張表的聚簇索引只允許存在一個,默認建表時(不建堆表的情況下),基表就是一個ROWID聚簇索引,可以預見到對ROWID的精准定位應該會走CSEK。
explain select c1 from t1 where rowid = 6;

用到的操作符說明:
NSET2 結果集(result set)收集,一般是查詢計划的頂層節點
PRJT2 關系的―投影‖(project)運算,用於選擇表達式項的計算
CSEK2 聚集索引數據定位
2) t1(c2)自定義聚簇索引i_index2
create cluster index i_index2 on t1(c2);
那么ROWID這個聚簇索引就不存在了,取而代指的是按C2為順序的聚簇索引
explain select c1 from t1 where rowid = 6;

用到的操作符說明:
NSET2 結果集(result set)收集,一般是查詢計划的頂層節點
PRJT2 關系的―投影‖(project)運算,用於選擇表達式項的計算
SLCT2 關系的―選擇‖(select)運算,用於查詢條件的過濾
SSCN 直接使用二級索引進行掃描
說明:這里查詢中需要C1以及ROWID,而普通二級索引I_TEST1上正好都有,且比聚簇索引的長度要短,所以選擇SSCN I_TEST1
explain select c1 from t1 where c2 = 6;

用到的操作符說明:
NSET2 結果集(result set)收集,一般是查詢計划的頂層節點
PRJT2 關系的―投影‖(project)運算,用於選擇表達式項的計算
CSEK2 聚集索引數據定位
五、復雜連接查詢
CREATE TABLE TEST5(ID INT); CREATE TABLE TEST6(ID INT); CREATE TABLE TEST7(ID INT); CREATE TABLE TEST8(ID INT); insert into test5 values(3); insert into test6 values(4); insert into test7 select level %100 from dual connect by level < 10000; insert into test8 select level %100 from dual connect by level < 10000; explain select /*+no_use_cvt_var*/* from (select test5.id from test5,test6 where test5.id = test6.id)a, (select id from (select test7.id from test7,test8 where test7.id = test8.id) group by id ) b where a.id = b.id;

用到的操作符說明:
NSET2 結果集(result set)收集,一般是查詢計划的頂層節點
PRJT2 關系的―投影‖(project)運算,用於選擇表達式項的計算
HASH2 INNER JOIN HASH 內連接
PRJT2 關系的―投影‖(project)運算,用於選擇表達式項的計算
HASH2 INNER JOIN HASH 內連接
PRJT2 關系的―投影‖(project)運算,用於選擇表達式項的計算
HASH2 INNER JOIN HASH 內連接
CSCN2 聚集索引掃描
CSCN2 聚集索引掃描
PRJT2 關系的―投影‖(project)運算,用於選擇表達式項的計算
HAGR2 HASH 分組,並計算聚集函數
PRJT2 關系的―投影‖(project)運算,用於選擇表達式項的計算
HASH2 INNER JOIN HASH 內連接
CSCN2 聚集索引掃描
CSCN2 聚集索引掃描
no_use_cvt_var 不考慮變量改寫方式實現連接,僅 OPTIMIZER_MODE=1 有效。

執行順序 6->7->5->12->13->11->9->3
首先執行TEST5和TEST6的HASH連接,然后執行TEST7,TEST8的HASH連接並將連接結果進行HASH分組,再將兩個結果再次進行HASH連接得到最終結果集。
六、多表連接的三種方式
一般來說:
等值連接條件一般會選擇哈希連接;
非等值連接條件會采用嵌套連接;
連接列均為索引列時,會采用歸並連接。
創建測試環境:
create table tab1(c1 int,c2 int ,c3 int); create table tab2(c1 int,c2 int ,c3 int); insert into tab1 select level,level,level from DUAL CONNECT by level <100000; insert into tab2 select level,level,level from DUAL CONNECT by level <100000; create index ind_tab1 on tab1(c1); create index ind_tab2 on tab2(c1); select * from user_indexes where table_name in ('TAB1','TAB2') ;
1、HASH JOIN(哈希連接)
Hash join的工作方式是將一個表(通常是小一點的那個表)做hash運算,將列數據存儲到hash列表中,從另一個表中抽取記錄,做hash運算,到hash 列表中找到相應的值,做匹配。
1)HASH2 INNER JOIN(HASH內連接)
explain select tab1.c1,tab2.c2 from tab1 , tab2 where tab1.c1=tab2.c1;

2)HASH LEFT JOIN2(HASH左外連接)
explain select tab1.c1 from tab1 left join tab2 on tab1.c1=tab2.c1;

3) HASH FULL JOIN2(HASH 全外連接)
explain select tab2.c1 from tab1 full outer join tab2 on tab2.c1=tab1.c1 ;

全連接的查詢結果是左外連接和右外連接查詢結果的並集,即使一些記錄關聯不上,也能夠把部分信息查詢出來。
4)HASH LEFT SEMI MULTIPLE JOIN(多列not in)
explain select * from tab1 where (c1,c2) not in (select c1,c2 from tab2) ;

5)HASH LEFT SEMI JOIN2(HASH 左半連接)
子查詢和非等值連接出現
explain select * from tab1 where c1 not in (select c1 from tab2)
and c2 not in (select c2 from tab2);

6)HASH RIGHT JOIN2(HASH右外連接)
explain select t.c1 ,tab1.c3 from tab1 left join (select * from tab2 where c1=10)t on t.c1=tab1.c2 ;

2、MERGE JOIN(歸並連接,也叫排序歸並連接)
Merge Join 是先將關聯表的關聯列各自做排序,然后從各自的排序表中抽取數據,到另一個排序表中做匹配,因為merge join需要做更多的排序,所以消耗的資源更多
1) 模擬:(歸並內連接)
連接列有索引且只需要返回索引列歸並更合適
explain select tab1.c1 from tab1 , tab2 where tab1.c1=tab2.c1;

3、NESTED LOOP(嵌套循環連接)
Nested loops 工作方式是從一張表中讀取數據,訪問另一張表(通常是索引)來做匹配,nested loops適用的場合是當一個關聯表比較小的時候,效率會更高。
1)NEST LOOP INNER JOIN2(嵌套循環內連接)
explain select tab1.c1 from tab1 ,tab2 where tab1.c2>tab2.c2;

2)NEST LOOP LEFT JOIN2(嵌套循環左連接)
explain select tab1.c1 from tab1 left join tab2 on tab1.c2>tab2.c2;

七、補充下索引的知識點
聚集(clustered)索引,也叫聚簇索引。聚簇索引的索引和數據是存儲在一起的。
定義:數據行的物理順序與列值(一般是主鍵的那一列)的邏輯順序相同,一個表中只能擁有一個聚集索引。
說實話,看着有點拗口,用大白話說就是,我們的sql數據庫是行數據庫,數據是一行一行存儲的,而聚集索引是個特殊的索引,相當於這一行行記錄的物理編號,描述這一行行數據的物理存儲順序。所以,一張表只會有一個聚集索引。
除了聚集索引外的其他索引類型都屬於二級索引。
非聚集索引和聚集索引的區別在於, 通過聚集索引可以查到需要查找的數據, 而通過非聚集索引可以查到記錄對應的主鍵值 , 再使用主鍵的值通過聚集索引查找到需要的數據
更多資訊請上達夢技術社區了解: https://eco.dameng.com
