SQL查詢的底層運(yùn)行原理深入分析
前言
SQL 語(yǔ)言無(wú)處不在。SQL 已經(jīng)不僅僅是技術(shù)人員的專屬技能了,似乎人人都會(huì)寫(xiě)SQL,就如同人人都是產(chǎn)品經(jīng)理一樣。如果你是做后臺(tái)開(kāi)發(fā)的,那么CRUD就是家常便飯。如果你是做數(shù)倉(cāng)開(kāi)發(fā)的,那么寫(xiě)SQL可能占據(jù)了你的大部分工作時(shí)間。我們?cè)诶斫?SELECT 語(yǔ)法的時(shí)候,還需要了解 SELECT 執(zhí)行時(shí)的底層原理。只有這樣,才能讓我們對(duì) SQL 有更深刻的認(rèn)識(shí)。本文分享將逐步分解SQL的執(zhí)行過(guò)程,希望對(duì)你有所幫助。
數(shù)據(jù)準(zhǔn)備
本文旨在說(shuō)明SQL查詢的執(zhí)行過(guò)程,不會(huì)涉及太復(fù)雜的SQL操作,主要涉及兩張表: citizen 和 city ,具體數(shù)據(jù)如下所示:
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",3), ("kevin",1), ("rachel",2), ("trump",3), ("lilei",1), ("hanmeiei",1);
查詢執(zhí)行順序
本文所涉及的查詢語(yǔ)句如下,主要是citizen表與city表進(jìn)行join,然后篩掉city_name != "上海"的數(shù)據(jù),接著按照city_name進(jìn)行分組,統(tǒng)計(jì)每個(gè)城市總?cè)藬?shù)大于2的城市,具體如下:
查詢語(yǔ)句
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
執(zhí)行步驟
上面SQL查詢語(yǔ)句的書(shū)寫(xiě)書(shū)序是:
SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY ...
但是執(zhí)行順序并不是這樣,具體的執(zhí)行順序如下步驟所示:
- 1.獲取數(shù)據(jù) ( From, Join )
- 2.過(guò)濾數(shù)據(jù) ( Where )
- 3.分組 ( Group by )
- 4.分組過(guò)濾 ( Having )
- 5.返回查詢字段 ( Select )
- 6.排序與分頁(yè) ( Order by & Limit / Offset )
尖叫提示:本文旨在說(shuō)明通用的SQL執(zhí)行底層原理,對(duì)于其優(yōu)化技術(shù)不做考慮,比如謂詞下推、投影下推等等。
執(zhí)行的底層原理
其實(shí)上面所說(shuō)的SQL執(zhí)行順序就是所謂的底層原理,當(dāng)我們?cè)趫?zhí)行SELECT語(yǔ)句時(shí),每個(gè)步驟都會(huì)產(chǎn)生一張 虛擬表(virtual table) ,在執(zhí)行下一步驟時(shí),會(huì)將該虛擬表作為輸入。指的注意的是,這些過(guò)程是對(duì)用戶透明的。
你可以注意到,SELECT 是先從FROM 這一步開(kāi)始執(zhí)行的。在這個(gè)階段,如果是多張表進(jìn)行JOIN,還會(huì)經(jīng)歷下面的幾個(gè)步驟:
獲取數(shù)據(jù) ( From, Join )
- 首先會(huì)通過(guò) CROSS JOIN 求笛卡爾積,相當(dāng)于得到虛擬表 vt1-1;
- 接著通過(guò)ON 條件進(jìn)行篩選,虛擬表 vt1-1 作為輸入,輸出虛擬表 vt1-2;
- 添加外部行。我們使用的是左連接、右鏈接或者全連接,就會(huì)涉及到外部行,也就是在虛擬表 vt1-2 的基礎(chǔ)上增加外部行,得到虛擬表 vt1-3
過(guò)濾數(shù)據(jù) ( Where )
經(jīng)過(guò)上面的步驟,我們得到了一張最終的虛擬表vt1,在此表之上作用where過(guò)濾,通過(guò)篩選條件過(guò)濾掉不滿足條件的數(shù)據(jù),從而得到虛擬表vt2。
分組 ( Group by )
經(jīng)過(guò)where過(guò)濾操作之后,得到vt2。接下來(lái)進(jìn)行GROUP BY操作,得到中間的虛擬表vt3。
分組過(guò)濾 ( Having )
在虛擬表vt3的基礎(chǔ)之上,使用having過(guò)濾掉不滿足條件的聚合數(shù)據(jù),得到vt4。
返回查詢字段 ( Select )
當(dāng)我們完成了條件篩選部分之后,就可以篩選表中提取的字段,也就是進(jìn)入到 SELECT 和 DISTINCT 階段。首先在 SELECT 階段會(huì)提取目標(biāo)字段,然后在 DISTINCT 階段過(guò)濾掉重復(fù)的行,分別得到中間的虛擬表 vt5-1 和 vt5-2。
排序與分頁(yè) ( Order by & Limit / Offset )
當(dāng)我們提取了想要的字段數(shù)據(jù)之后,就可以按照指定的字段進(jìn)行排序,也就是 ORDER BY 階段,得到虛擬表 vt6。最后在 vt6 的基礎(chǔ)上,取出指定行的記錄,也就是 LIMIT 階段,得到最終的結(jié)果,對(duì)應(yīng)的是虛擬表 vt7
詳細(xì)執(zhí)行步驟分析
Step 1:獲取數(shù)據(jù) ( From, Join )
FROM citizen JOIN city
該過(guò)程的第一步是執(zhí)行From子句中的語(yǔ)句,然后執(zhí)行Join子句。這些操作的結(jié)果是得到兩個(gè)表的笛卡爾積。
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執(zhí)行結(jié)束之后,會(huì)按照J(rèn)OIN的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:過(guò)濾數(shù)據(jù) ( Where )
獲得滿足條件的行后,將傳遞給Where子句。這將使用條件表達(dá)式評(píng)估每一行。如果行的計(jì)算結(jié)果不為true,則會(huì)將其從集合中刪除。
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 )
下一步是執(zhí)行Group by子句,它將具有相同值的行分為一組。此后,將按組對(duì)所有Select表達(dá)式進(jìn)行評(píng)估,而不是按行進(jìn)行評(píng)估。
GROUP BY city.city_name
GROUP_CONCAT(citizen. name ) | city_id | city_name |
---|---|---|
jack,rachel | 2 | 北京 |
tom,jasper,trump | 3 | 杭州 |
Step 4:分組過(guò)濾 ( Having )
對(duì)分組后的數(shù)據(jù)使用Having子句所包含的謂詞進(jìn)行過(guò)濾
HAVING COUNT(*) >= 2
Step 5:返回查詢字段 ( Select )
在此步驟中,處理器將評(píng)估查詢結(jié)果將要打印的內(nèi)容,以及是否有一些函數(shù)要對(duì)數(shù)據(jù)運(yùn)行,例如Distinct,Max,Sqrt,Date,Lower等等。本案例中,SELECT子句只會(huì)打印城市名稱和其對(duì)應(yīng)分組的count(*)值,并使用標(biāo)識(shí)符“ City”作為city_name列的別名。
SELECT city.city_name AS "City", COUNT(*) AS "citizen_cnt"
city | citizen_cnt |
---|---|
北京 | 2 |
杭州 | 3 |
Step 6:排序與分頁(yè) ( Order by & Limit / Offset )
查詢的最后處理步驟涉及結(jié)果集的排序與輸出大小。在我們的示例中,按照字母順序升序排列,并輸出兩條數(shù)據(jù)結(jié)果。
ORDER BY city.city_name ASC LIMIT 2
city | citizen_cnt |
---|---|
北京 | 2 |
杭州 | 3 |
總結(jié)
本文主要剖析了SQL語(yǔ)句的執(zhí)行順序和底層原理,基本的SQL查詢會(huì)分為六大步驟。本文結(jié)合具體事例,給出了每一步驟的詳細(xì)結(jié)果,這樣會(huì)對(duì)其執(zhí)行的底層原理有更加深刻的認(rèn)識(shí)。
到此這篇關(guān)于SQL查詢的底層運(yùn)行原理深入分析的文章就介紹到這了,更多相關(guān)SQL查詢底層運(yùn)行原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
neo4j創(chuàng)建數(shù)據(jù)庫(kù)以及導(dǎo)入csv文件內(nèi)容圖文詳解
這篇文章主要給大家介紹了關(guān)于neo4j創(chuàng)建數(shù)據(jù)庫(kù)以及導(dǎo)入csv文件內(nèi)容的相關(guān)資料,Neo4j是一個(gè)基于圖形結(jié)構(gòu)的NoSQL數(shù)據(jù)庫(kù),它提供了一種高效的方式來(lái)管理和查詢大型復(fù)雜數(shù)據(jù),需要的朋友可以參考下2023-11-11數(shù)據(jù)庫(kù)設(shè)計(jì)的折衷方法
這篇文章主要介紹了數(shù)據(jù)庫(kù)設(shè)計(jì)的折衷方法,需要的朋友可以參考下2007-03-03使用 Navicat 創(chuàng)建數(shù)據(jù)庫(kù)并用JDBC連接的操作方法
這篇文章主要介紹了使用 Navicat 創(chuàng)建數(shù)據(jù)庫(kù)并用JDBC連接的操作方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11explain慢查詢SQL調(diào)優(yōu)exists的實(shí)戰(zhàn)
這篇文章主要介紹了explain慢查詢SQL調(diào)優(yōu)exists的實(shí)戰(zhàn),經(jīng)過(guò)兩次優(yōu)化SQL語(yǔ)句之后,慢SQL的性能顯著提升了,耗時(shí)從8s優(yōu)化到了0.7s,現(xiàn)在拿出來(lái)給大家分享一下,希望對(duì)你會(huì)有所幫助2023-12-12Linux下 mysql oracle 簡(jiǎn)單使用手冊(cè)
1.linux下使用Mysql與oracle的 簡(jiǎn)單說(shuō)明2009-06-06分布式數(shù)據(jù)存儲(chǔ)系統(tǒng)的三要素
大家好,本篇文章主要講的是分布式數(shù)據(jù)存儲(chǔ)系統(tǒng)的三要素,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽2021-12-12pentaho工具將數(shù)據(jù)庫(kù)數(shù)據(jù)導(dǎo)入導(dǎo)出為Excel圖文步驟
本篇博客講述的是如何使用pentaho工具快速的將數(shù)據(jù)庫(kù)數(shù)據(jù)導(dǎo)出為Excel文件,以及如何將Excel文件數(shù)據(jù)導(dǎo)入數(shù)據(jù)庫(kù),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-03-03本機(jī)上實(shí)現(xiàn)neo4j遠(yuǎn)程連接方式
這篇文章主要介紹了本機(jī)上實(shí)現(xiàn)neo4j遠(yuǎn)程連接方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-02-02