一、SQL語句執行順序
結合上圖,整理出如下偽 SQL 查詢語句。
從這個順序中可以發現
所有的查詢語句都是從 FROM 開始執行的。在實際執行過程中,每個步驟都會為下一個步驟生成一個虛擬表,這個虛擬表將作為下一個執行步驟的輸入。
接下來,我們詳細的介紹下每個步驟的具體執行過程。
二、FROM:執行笛卡爾積
FROM 才是 SQL 語句執行的第一步,並非 SELECT 。對 FROM 子句中的前兩個表執行笛卡爾積(交叉聯接),生成虛擬表 VT1
,獲取不同數據源的數據集。
FROM 子句執行順序為從后往前、從右到左,FROM 子句中寫在最后的表(基礎表 driving table)將被最先處理,即最后的表為驅動表,當 FROM 子句中包含多個表的情況下,我們需要選擇數據最少的表作為基礎表。
三、ON:應用ON過濾器
對虛擬表 VT1 應用 ON 篩選器,ON 中的邏輯表達式將應用到虛擬表 VT1 中的各個行,篩選出滿足 ON 邏輯表達式的行,生成虛擬表 VT2
。
三、JOIN:添加外部行
如果指定了 OUTER JOIN 保留表中未找到匹配的行將作為外部行添加到虛擬表 VT2,生成虛擬表 VT3
。保留表如下:
- LEFT OUTER JOIN 把左表記為保留表
- RIGHT OUTER JOIN 把右表記為保留表
- FULL OUTER JOIN 把左右表都作為保留表
在虛擬表 VT2 表的基礎上添加保留表中被過濾條件過濾掉的數據,非保留表中的數據被賦予 NULL 值,最后生成虛擬表 VT3
。
如果 FROM 子句包含兩個以上的表,則對上一個聯接生成的結果表和下一個表重復執行步驟1~3,直到處理完所有的表為止。
四、WHERE:應用 WEHRE 過濾器
對虛擬表 VT3 應用 WHERE 篩選器。根據指定的條件對數據進行篩選,並把滿足的數據插入虛擬表 VT4
。
- 由於數據還沒有分組,因此現在還不能在 WHERE 過濾器中使用聚合函數對分組統計的過濾。
- 同時,由於還沒有進行列的選取操作,因此在SELECT中使用列的別名也是不被允許的。
五、GROUP BY:分組
按 GROUP BY 子句中的列/列表將虛擬表 VT4 中的行唯一的值組合成為一組,生成虛擬表 VT5
。如果應用了 GROUP BY,那么后面的所有步驟都只能得到的虛擬表 VT5 的列或者是聚合函數(count、sum、avg等)。原因在於最終的結果集中只為每個組包含一行。
同時,從這一步開始,后面的語句中都可以使用 SELECT 中的別名。
六、AGG_FUNC:計算聚合函數
計算 max 等聚合函數。SQL Aggregate 函數計算從列中取得的值,返回一個單一的值。常用的 Aggregate 函數包涵以下幾種:
- AVG:返回平均值
- COUNT:返回行數
- FIRST:返回第一個記錄的值
- LAST:返回最后一個記錄的值
- MAX: 返回最大值
- MIN:返回最小值
- SUM: 返回總和
七、WITH:應用 ROLLUP 或 CUBE
對虛擬表 VT5 應用 ROLLUP 或 CUBE 選項,生成虛擬表 VT6
。
CUBE 和 ROLLUP 區別如下:
- CUBE 生成的結果數據集顯示了所選列中值的所有組合的聚合。
- ROLLUP 生成的結果數據集顯示了所選列中值的某一層次結構的聚合。
八、HAVING:應用 HAVING 過濾器
對虛擬表 VT6 應用 HAVING 篩選器。根據指定的條件對數據進行篩選,並把滿足的數據插入虛擬表 VT7
。
HAVING 語句在 SQL 中的主要作用與 WHERE 語句作用是相同的,但是 HAVING 是過濾聚合值,在 SQL 中增加 HAVING 子句原因就是,WHERE 關鍵字無法與聚合函數一起使用,HAVING 子句主要和 GROUP BY 子句配合使用。
九、SELECT:選出指定列
將虛擬表 VT7 中的在 SELECT 中出現的列篩選出來,並對字段進行處理,計算 SELECT 子句中的表達式,產生虛擬表 VT8
。
十、DISTINCT:行去重
將重復的行從虛擬表 VT8 中移除,產生虛擬表 VT9
。DISTINCT 用來刪除重復行,只保留唯一的。
十一、ORDER BY:排列
將虛擬表 VT9 中的行按 ORDER BY 子句中的列/列表排序,生成游標 VC10
,注意不是虛擬表。因此使用 ORDER BY 子句查詢不能應用於表達式。同時,ORDER BY 子句的執行順序為從左到右排序,是非常消耗資源的。
十二、LIMIT/OFFSET:指定返回行
從 VC10 的開始處選擇指定數量行,生成虛擬表 VT11
,並返回調用者。
十三、實例
接下來,我們看一個實例,以下 SQL 查詢語句是否存在問題?
首先,我們先看下如上 SQL 的執行順序,如下:
- 首先執行 FROM 子句, 從學生成績表中組裝數據源的數據。
- 執行 WHERE 子句, 篩選學生成績表中所有學生的數學成績不為 NULL 的數據 。
- 執行 GROUP BY 子句, 把學生成績表按 "班級" 字段進行分組。
- 計算 avg 聚合函數, 按找每個班級分組求出 數學平均成績。
- 執行 HAVING 子句, 篩選出班級 數學平均成績大於 75 分的。
- 執行 SELECT 語句,返回數據,但別着急,還需要執行后面幾個步驟。
- 執行 ORDER BY 子句, 把最后的結果按 "數學平均成績" 進行排序。
- 執行 LIMIT ,限制僅返回3條數據。結合 ORDER BY 子句,即返回所有班級中數學平均成績的前三的班級及其數學平均成績。
思考一下,如果我們將上面語句改成,如下會怎樣?
我們發現,若將 avg(數學成績) > 75 放到 WHERE 子句中,此時 GROUP BY 語句還未執行,因此此時聚合值 avg(數學成績) 還是未知的,因此會報錯。