SQL查詢的底層執行原理深入分析
前言
SQL 語言無處不在。SQL 已經不僅僅是技術人員的專屬技能了,似乎人人都會寫SQL,就如同人人都是產品經理一樣。如果你是做後臺開發的,那麼CRUD就是家常便飯。如果你是做數倉開發的,那麼寫SQL可能佔據了你的大部分工作時間。我們在理解 SELECT 語法的時候,還需要了解 SELECT 執行時的底層原理。只有這樣,才能讓我們對 SQL 有更深刻的認識。本文分享將逐步分解SQL的執行過程,希望對你有所幫助。
資料準備
本文旨在說明SQL查詢的執行過程,不會涉及太複雜的SQL操作,主要涉及兩張表: citizen 和 city,具體資料如下所示:
CREATE TABLE citizen ( name CHAR ( 20 ),city_id INT ( 10 ) ); CREATE TABLE city ( city_id INT ( 10 ),city_name CHAR ( 20 ) ); INSERT INTO city VALUES ( 1,"上海" ),( 2,"北京" ),( 3,"杭州" ); INSERT INTO citizen VALUES ("tom",3),("jack",2),("robin",1),("jasper",("kevin",("rachel",("trump",("lilei",("hanmeiei",1);
查詢執行順序
本文所涉及的查詢語句如下,主要是citizen表與city表進行join,然後篩掉city_name != "上海"的資料,接著按照city_name進行分組,統計每個城市總人數大於2的城市,具體如下:
查詢語句
SELECT city.city_name AS "City",COUNT(*) AS "citizen_cnt" FROM citizen JOIN city ON citizen.city_id = city.city_id WHERE city.city_name != '上海' GROUP BY city.city_name HAVING COUNT(*) >= 2 ORDER BY city.city_name ASC LIMIT 2
執行步驟
上面SQL查詢語句的書寫書序是:
SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY ...
但是執行順序並不是這樣,具體的執行順序如下步驟所示:
- 1.獲取資料 ( From,Join )
- 2.過濾資料 ( Where )
- 3.分組 ( Group by )
- 4.分組過濾 ( Having )
- 5.返回查詢欄位 ( Select )
- 6.排序與分頁 ( Order by & Limit / Offset )
尖叫提示:本文旨在說明通用的SQL執行底層原理,對於其優化技術不做考慮,比如謂詞下推、投影下推等等。
執行的底層原理
其實上面所說的SQL執行順序就是所謂的底層原理,當我們在執行SELECT語句時,每個步驟都會產生一張 虛擬表(virtual table) ,在執行下一步驟時,會將該虛擬表作為輸入。指的注意的是,這些過程是對使用者透明的。
你可以注意到,SELECT 是先從FROM 這一步開始執行的。在這個階段,如果是多張表進行JOIN,還會經歷下面的幾個步驟:
獲取資料 ( From,Join )
- 首先會通過 CROSS JOIN 求笛卡爾積,相當於得到虛擬表 vt1-1;
- 接著通過ON 條件進行篩選,虛擬表 vt1-1 作為輸入,輸出虛擬表 vt1-2;
- 新增外部行。我們使用的是左連線、右連結或者全連線,就會涉及到外部行,也就是在虛擬表 vt1-2 的基礎上增加外部行,得到虛擬表 vt1-3
過濾資料 ( Where )
經過上面的步驟,我們得到了一張最終的虛擬表vt1,在此表之上作用where過濾,通過篩選條件過濾掉不滿足條件的資料,從而得到虛擬表vt2。
分組 ( Group by )
經過where過濾操作之後,得到vt2。接下來進行GROUP BY操作,得到中間的虛擬表vt3。
分組過濾 ( Having )
在虛擬表vt3的基礎之上,使用having過濾掉不滿足條件的聚合資料,得到vt4。
返回查詢欄位 ( Select )
當我們完成了條件篩選部分之後,就可以篩選表中提取的欄位,也就是進入到 SELECT 和 DISTINCT 階段。首先在 SELECT 階段會提取目標欄位,然後在 DISTINCT 階段過濾掉重複的行,分別得到中間的虛擬表 vt5-1 和 vt5-2。
排序與分頁 ( Order by & Limit / Offset )
當我們提取了想要的欄位資料之後,就可以按照指定的欄位進行排序,也就是 ORDER BY 階段,得到虛擬表 vt6。最後在 vt6 的基礎上,取出指定行的記錄,也就是 LIMIT 階段,得到最終的結果,對應的是虛擬表 vt7
詳細執行步驟分析
Step 1:獲取資料 ( From,Join )
FROM citizen JOIN city
該過程的第一步是執行From子句中的語句,然後執行Join子句。這些操作的結果是得到兩個表的笛卡爾積。
name | city_id | city_id | city_name |
---|---|---|---|
tom | 3 | 1 | 上海 |
tom | 3 | 2 | 北京 |
tom | 3 | 3 | 杭州 |
jack | 2 | 1 | 上海 |
jack | 2 | 2 | 北京 |
jack | 2 | 3 | 杭州 |
robin | 1 | 1 | 上海 |
robin | 1 | 2 | 北京 |
robin | 1 | 3 | 杭州 |
jasper | 3 | 1 | 上海 |
jasper | 3 | 2 | 北京 |
jasper | 3 | 3 | 杭州 |
kevin | 1 | 1 | 上海 |
kevin | 1 | 2 | 北京 |
kevin | 1 | 3 | 杭州 |
rachel | 2 | 1 | 上海 |
rachel | 2 | 2 | 北京 |
rachel | 2 | 3 | 杭州 |
trump | 3 | 1 | 上海 |
trump | 3 | 2 | 北京 |
trump | 3 | 3 | 杭州 |
lilei | 1 | 1 | 上海 |
lilei | 1 | 2 | 北京 |
lilei | 1 | 3 | 杭州 |
hanmeiei | 1 | 1 | 上海 |
hanmeiei | 1 | 2 | 北京 |
hanmeiei | 1 | 3 | 杭州 |
在FROM和JOIN執行結束之後,會按照JOIN的ON條件,篩選所需要的行
ON citizen.city_id = city.city_id
name | city_id | city_id | city_name |
---|---|---|---|
tom | 3 | 3 | 杭州 |
jack | 2 | 2 | 北京 |
robin | 1 | 1 | 上海 |
jasper | 3 | 3 | 杭州 |
kevin | 1 | 1 | 上海 |
rachel | 2 | 2 | 北京 |
trump | 3 | 3 | 杭州 |
lilei | 1 | 1 | 上海 |
hanmeiei | 1 | 1 | 上海 |
Step 2:過濾資料 ( Where )
獲得滿足條件的行後,將傳遞給Where子句。這將使用條件表示式評估每一行。如果行的計算結果不為true,則會將其從集合中刪除。
WHERE city.city_name != '上海'
name | city_id | city_id | city_name |
---|---|---|---|
tom | 3 | 3 | 杭州 |
jack | 2 | 2 | 北京 |
jasper | 3 | 3 | 杭州 |
rachel | 2 | 2 | 北京 |
trump | 3 | 3 | 杭州 |
Step 3:分組 ( Group by )
下一步是執行Group by子句,它將具有相同值的行分為一組。此後,將按組對所有Select表示式進行評估,而不是按行進行評估。
GROUP BY city.city_name
GROUP_CONCAT(citizen.name) | city_id | city_name |
---|---|---|
jack,rachel | 2 | 北京 |
tom,jasper,trump | 3 | 杭州 |
Step 4:分組過濾 ( Having )
對分組後的資料使用Having子句所包含的謂詞進行過濾
HAVING COUNT(*) >= 2
Step 5:返回查詢欄位 ( Select )
在此步驟中,處理器將評估查詢結果將要列印的內容,以及是否有一些函式要對資料執行,例如Distinct,Max,Sqrt,Date,Lower等等。本案例中,SELECT子句只會列印城市名稱和其對應分組的count(*)值,並使用識別符號“ City”作為city_name列的別名。
SELECT city.city_name AS "City",COUNT(*) AS "citizen_cnt"
city | citizen_cnt |
---|---|
北京 | 2 |
杭州 | 3 |
Step 6:排序與分頁 ( Order by & Limit / Offset )
查詢的最後處理步驟涉及結果集的排序與輸出大小。在我們的示例中,按照字母順序升序排列,並輸出兩條資料結果。
ORDER BY city.city_name ASC LIMIT 2
city | citizen_cnt |
---|---|
北京 | 2 |
杭州 | 3 |
總結
本文主要剖析了SQL語句的執行順序和底層原理,基本的SQL查詢會分為六大步驟。本文結合具體事例,給出了每一步驟的詳細結果,這樣會對其執行的底層原理有更加深刻的認識。
到此這篇關於SQL查詢的底層執行原理深入分析的文章就介紹到這了,更多相關SQL查詢底層執行原理內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!