mysql中sql的執行順序的坑
阿新 • • 發佈:2019-01-31
今天工作中碰到一個sql問題,關於left join的,後面雖然解決了,但是通過此問題了解了一下sql的執行順序
場景還原
為避免安全糾紛,把場景模擬。
有一個學生表-S,一個成績表G
CREATE TABLE `test_student` ( `id` bigint(20) NOT NULL COMMENT '學號', `sex` TINYINT DEFAULT '0' COMMENT '性別 0-男 1-女', `name` varchar(255) DEFAULT NULL COMMENT '姓名' ) ENGINE=InnoDB CHARSET=utf8mb4 COMMENT='學生表'; CREATE TABLE `test_score` ( `id` bigint(20) NOT NULL COMMENT '學號', `score` int NOT NULL COMMENT '分數', `level` TINYINT COMMENT '成績 0-不及格 1-及格 2-優良 3-優秀' ) ENGINE=InnoDB CHARSET=utf8mb4 COMMENT='成績表'; -- 初始化學生 INSERT INTO test_student VALUES(1, 0, '張三'), (2, 0, '李四'), (3, 1, '王X芳');
-- 初始化成績
INSERT INTO test_score VALUES(1, 10, 0), (2, 20, 0), (3, 100, 3);
現在有一個需求,查出學生的考試成績,可能的sql
SELECT ts.name AS '名字', tc.score AS '分數'
FROM test_student ts
LEFT JOIN test_score tc
ON ts.id = tc.s_id;
,一切都很順利
突然,剛考完試,就轉來一個學生
INSERT INTO test_student VALUES(4, 0, '新來的');
他沒成績,還是剛才的sql查
,老師只要看參加考試的學生的成績,怎麼辦
1.用inner join
SELECT ts.name AS '名字', tc.score AS '分數'
FROM test_student ts
INNER JOIN test_score tc
ON ts.id = tc.s_id;
2.加條件過濾
SELECT ts.name AS '名字', tc.score AS '分數'
FROM test_student ts
LEFT JOIN test_score tc
ON ts.id = tc.s_id
AND tc.score is NOT NULL
;
,發現加了條件過濾還是不對,額,改用where呢
SELECT ts.name AS '名字', tc.score AS '分數' FROM test_student ts LEFT JOIN test_score tc ON ts.id = tc.s_id WHERE tc.score is NOT NULL ;
,bingo,那where為什麼就對了呢。
這就涉及到sql的執行順序了
從上面的例子推斷
- where會從結果集中過濾出符合條件的記錄,不符合條件的丟棄
join操作:有時為了得到完整的結果,我們需要從兩個或更多的表中獲取結果。我們就需要執行 join。
除了我們在上面的例子中使用的 INNER JOIN(內連線),我們還可以使用其他幾種連線。
下面列出了您可以使用的 JOIN 型別,以及它們之間的差異。
- JOIN: 如果表中有至少一個匹配,則返回行
- LEFT JOIN: 即使右表中沒有匹配,也從左表返回所有的行
- RIGHT JOIN: 即使左表中沒有匹配,也從右表返回所有的行
- FULL JOIN: 只要其中一個表中存在匹配,就返回行(mysql不支援,可以考慮用檢視實現)
sql順序
從上面看到,在sql中,on是寫在了where條件之前,那麼資料庫引擎分析執行sql時,是否on也是在where前面呢?
一般sql的寫法順序
- SELECT [列名稱 *代表所有的列]
- FROM [表名稱]
- join_type JOIN [表名稱]
- ON [join條件]
- WHERE [過濾條件]
- GROUP BY [分組欄位]
- HAVING [分組條件]
- ORDER BY [排序欄位]
那麼sql在執行時,順序是怎樣的呢?
標準的sql解析順序為:
- FROM 組裝資料,來自不同資料來源(表)
- WHERE 根據條件過濾記錄
- GROUP BY 對資料分組
- 計算聚集函式,如avg,sum
- 使用HAVING子句篩選分組
- 計算所有表示式
- 使用ORDER BY對結果排序
那麼sql的執行順序呢?
- FROM: 對前2個表執行笛卡爾積,生成虛表vt1
- ON: 對vt1應用on條件,只有滿足join_condition條件的才能插入虛表vt2
- OUTER(join):如果指定了 OUTER JOIN保留表(preserved table)中未找到的行將行作為外部行新增到vt2,生成t3,如果from包含兩個以上表,則對上一個聯結生成的結果表和下一個表重複執行步驟和步驟直接結束
- WHERE: 對vt3進行where篩選,只有滿足where條件的才能插入vt4
- GROUP BY: 對vt4按group by欄位分組,得到vt5
- HAVING:對vt5應用HAVING篩選器只有使 having_condition 為true的組才插入vt6
- SELECT:處理select列表產生vt7
- DISTINCT:將重複的行從vt7中去除產生vt8
- ORDER BY:將vt8的行按order by子句中的列 列表排序生成一個遊標vc9
- LIMIT(Mysql): 從vc9的開始處選擇指定數量的行生成vt10 並返回呼叫者
到了這裡,應該發現,要寫好sql不容易。但是瞭解了sql的執行順序,能在開發的同理,更好的幫助寫出好的程式。
比如join表不能太多(先過濾條件然後再根據表連線 同時在表中建立相關查詢欄位的索引這樣在大資料多表聯合查詢的情況下速度相當快),這種sql優化問題,下次再學習整理下。
實踐之!