一條SQL語(yǔ)句的“一生”
點(diǎn)下方關(guān)注“SQL數(shù)據(jù)庫(kù)開(kāi)發(fā)”,
設(shè)為“置頂或星標(biāo)”,第一時(shí)間送達(dá)干貨
SQL語(yǔ)言相信大家都不陌生,從本質(zhì)上來(lái)說(shuō),它是一種結(jié)構(gòu)化查詢語(yǔ)言,是用來(lái)數(shù)據(jù)庫(kù)之間的通信的編程語(yǔ)言。作為一名Java程序員,我們從Java角度來(lái)看,SQL語(yǔ)言相當(dāng)于Java接口,而數(shù)據(jù)庫(kù)是實(shí)現(xiàn)這個(gè)接口的實(shí)現(xiàn)類,SQL語(yǔ)句則是實(shí)現(xiàn)類的方法??!。從這里我們就可以理解了,每個(gè)數(shù)據(jù)庫(kù)都有著自己獨(dú)特的規(guī)則,但大體上是遵循SQL標(biāo)準(zhǔn)。
SQL 語(yǔ)句有一個(gè)讓大部分人都感到困惑的地方,就是我寫(xiě)的 SQL 語(yǔ)句的跟我預(yù)想要的結(jié)果不一樣。在這里,我們就以 Mysql 數(shù)據(jù)庫(kù)為例,對(duì)一條 SQL 語(yǔ)句的執(zhí)行順序進(jìn)行分析。
接下來(lái)再走一步,讓我們看看一條 SQL 語(yǔ)句的前世今生。
首先看一下示例語(yǔ)句

它的執(zhí)行順序是這樣的

準(zhǔn)備數(shù)據(jù)
我們會(huì)先準(zhǔn)備一些數(shù)據(jù),即創(chuàng)建 classes、student 表,并插入測(cè)試數(shù)據(jù), SQL語(yǔ)句如下:


OK,有了數(shù)據(jù)之后,我們就可以來(lái)看看 SQL 語(yǔ)句在 MySQL 中執(zhí)行過(guò)程了,SQL 語(yǔ)句如下:

SQL執(zhí)行之旅
可能你現(xiàn)在還對(duì) Mysql 語(yǔ)句的執(zhí)行順序一知半解,沒(méi)關(guān)系,下來(lái)我將按 SQL 執(zhí)行的順序詳細(xì)介紹每個(gè)關(guān)鍵字的作用,以及注意的地方。
1、FROM:首先,是從 From 開(kāi)始的,對(duì)FROM子句中的前兩個(gè)表執(zhí)行笛卡爾積(交叉聯(lián)接),生成虛擬表VT1。

2、ON:對(duì)VT1應(yīng)用ON篩選器,只有那些使為真才被插入到VT2。ON不能單獨(dú),在這里你可以把ON理解為WHERE。

3、JOIN:如果指定了OUTER JOIN(相對(duì)于CROSS JOIN或INNER JOIN),保留表(主表)中不符合ON條件匹配的行將作為外部行添加到VT2,生成VT3。如果FROM子句超過(guò)兩個(gè)表,上一個(gè)聯(lián)接生成的結(jié)果表會(huì)和下一個(gè)表重復(fù)執(zhí)行步驟1到步驟3,直到處理完所有的表的關(guān)聯(lián)。

4、WHERE:對(duì)VT3應(yīng)用WHERE篩選器,只有為true的行才插入VT4。

5、GROUP BY:按GROUP BY子句中的列列表對(duì)VT4中的行進(jìn)行分組,生成VT5。

在這里會(huì)有一個(gè)奇怪的現(xiàn)象,MySQL執(zhí)行順序GROUP BY -> HAVING -> SELECT,從順序看SELECL在GROUP BY之后,GROUP BY 應(yīng)該不可以使用SELECT字段別名,但是在GROUP BY卻可以使用SELECT字段別名,主要原因MySQL擴(kuò)展了標(biāo)準(zhǔn)SQL,允許GROUP BY子句使用的SELECT子句中的別名以及和非列表表達(dá)式等標(biāo)準(zhǔn), 并認(rèn)為語(yǔ)句是有效的。從MySQL 5.7.5開(kāi)始,默認(rèn)SQL mode模式包括 ONLY_FULL_GROUP_BY。(在5.7.5之前,MySQL不檢測(cè)功能依賴性,ONLY_FULL_GROUP_BY默認(rèn)情況下不啟用。
6、HAVING :對(duì)VT5應(yīng)用HAVING篩選器,只有為true的組插入到VT6。

HAVING同GROUP BY一樣,MySQL拓展SQL標(biāo)準(zhǔn)以允許HAVING可以使用別名和非列表表達(dá)式。
7、SELECT:將VT6每一組數(shù)據(jù)執(zhí)行select xx,有幾組就執(zhí)行幾次,產(chǎn)生VT7。

這里有一點(diǎn)要注意,當(dāng)SQL mode 模式ONLY_FULL_GROUP_BY不開(kāi)啟,不會(huì)強(qiáng)制SELECT指定的字段必須屬于GROUP BY后的條件。若符合條件的字段有多個(gè),則只顯示第一次出現(xiàn)的字段。雖然這種查詢?cè)谡Z(yǔ)法上通過(guò)了,但結(jié)果并沒(méi)有什么意義,因?yàn)槠渌侄尾⒎切枰臏?zhǔn)確值。所以最好SELECT語(yǔ)句指定的字段必須是“分組依據(jù)字段”。
8、ORDER BY:將VT7中的行按ORDER BY子句中的列列表順序,ORDER BY只能選擇SELECT的字段

9、LIMIT:從VT7的開(kāi)始處選擇指定數(shù)量或比例的行,生成表VT8,并返回給調(diào)用者。

OK,到這里就執(zhí)行結(jié)束了。我們可以發(fā)現(xiàn),SQL 語(yǔ)句的語(yǔ)法順序和執(zhí)行順序并不一致,如果你已經(jīng)可以清醒知道它們之間差異,你就可以看出為什么以前寫(xiě)的SQL總是和我們預(yù)想的不一致。你看,哪怕只有一條小小的 SQL 語(yǔ)句都有這么多門道,只有不斷專研探究,我們才可以真正掌握這一門技術(shù)。
這里多提一下,在SQL語(yǔ)法有幾點(diǎn)要特別注意,SELECT雖然在GROUP BY和HAVING 之后,但是如果SQL mode模式 ONLY_FULL_GROUP_BY不開(kāi)啟,GROUP BY和HAVING是允許使用SELECT的字段,而且也不會(huì)強(qiáng)制SELECT指定的字段必須屬于GROUP BY后的條件。至此SQL的解析之旅就結(jié)束了,最后用一張圖總結(jié)一下今天的內(nèi)容:


點(diǎn)擊右下角「在看」和轉(zhuǎn)發(fā)
是對(duì)我們最大的支持
最后給大家分享我寫(xiě)的SQL兩件套:《SQL基礎(chǔ)知識(shí)第二版》和《SQL高級(jí)知識(shí)第二版》的PDF電子版。里面有各個(gè)語(yǔ)法的解釋、大量的實(shí)例講解和批注等等,非常通俗易懂,方便大家跟著一起來(lái)實(shí)操。
有需要的讀者可以下載學(xué)習(xí),在下面的公眾號(hào)「數(shù)據(jù)前線」(非本號(hào))后臺(tái)回復(fù)關(guān)鍵字:SQL,就行
數(shù)據(jù)前線
后臺(tái)回復(fù)關(guān)鍵字:1024,獲取一份精心整理的技術(shù)干貨
后臺(tái)回復(fù)關(guān)鍵字:進(jìn)群,帶你進(jìn)入高手如云的交流群。
