前言
說起數據庫,想必一些朋友會認為,數據庫不就是天天CRUD嗎?只要我掌握了這幾招,根本不在話下。是的,其實我也很贊同這個觀點,對于大多數應用程序來說,只掌握這些內容,是可以勝任日常的開發工作的。
然而我們的程序不可能始終停留在測試環境,隨著生產環境數據量的增大,以及用戶規模的增長,數據庫就會出現一些性能方面的問題。所以接下來的一段時間內,我們會學習一些數據庫性能優化方面的內容,希望可以帶給大家一些啟迪和成長。今天,我們就從最基本的子查詢講起。
概述
子查詢的定義本身很簡單,就是一種嵌套查詢。最常見的例子就是我們有兩張表students和classes,為了簡化說明,此處沒有遵守三大范式,表中的字段如下:
sutdents
id | stu_name | stu_age | class_id |
1 | 張三 | 15 | 1002 |
2 | 李四 | 15 | 1001 |
3 | 王五 | 16 | 1002 |
4 | 朱六 | 14 | 1003 |
classes
class_id | class_name |
1001 | 語文 |
1002 | 數學 |
1003 | 英語 |
此時我們想查詢哪些學生選擇了數學課,就可以使用子查詢去查找結果:
SELECT stu_name FROM sutdents WHERE sutdents.class_id IN (SELECT class_id FROM classes WHERE class_name = '數學')
可以看出,我們在這里使用了IN操作符來關聯子查詢的結果,熟悉的伙伴一定清楚,還有一種子查詢的方式叫做EXISTS,使用方式如下:
SELECT stu_name FROM sutdents s WHERE EXISTS (SELECT 1 FROM classes c WHERE s.class_id = c.class_id AND class_name = '數學')
兩種方式的查詢結果都是一致的,我們找出了哪些學生選擇了數學課:
但實際上,這兩種子查詢的效率在不同的情況下是不一樣的,下面我們來單獨談談這兩種方式的區別。
什么是IN
從字面意思來看,IN即為外部表的字段,使用內部嵌套查詢的結果中作為條件去查詢所需的結果。因此,使用IN時,內部嵌套的子查詢只會查詢一次,這里引申出一個概念,叫做:非關聯子查詢。
在我們上述的示例中,子查詢會先從classes表中找出課程名稱為“數學”的id,此時子查詢只執行了一次。通過查詢的結果,即數學課的id號,作為外部查詢的條件,去查找最終的結果。
什么是EXISTS
同樣的,顧名思義,EXISTS即為“存在”。也就是說,在嵌套查詢時,外部表的信息,需要在內部表的結果中存在,才可以查詢出結果。因此,使用EXISTS時,內部嵌套的子查詢會查詢多次,次數取決于外部表的記錄條目,最終返回對應的結果給外部表,這個概念叫做:關聯子查詢。
在上述示例中,查詢語句會先從students表中找出每一條記錄對應的class_id,然后傳遞給內部的子查詢,內部子查詢使用該class_id進行匹配,去classes表中查找滿足class_id和class_name為“數學”的記錄,再返回給外部的表。在此過程中,只有完全匹配的記錄,才會呈現最終的結果。
如何優化子查詢
在以往的很多經驗和一些文章中經常會提到,優化SQL語句,需要使用EXISTS來代替IN,即可達到效果。那么這句話究竟是不是正確的呢?
通過今天的學習,我們現在已經知道,這句話不完全正確,也不完全錯誤。究其根因,我們已經了解了IN和EXISTS的特性,所以結論如下:
1、如果一個語句中,子查詢外部的表A,數量小于子查詢內部的表B,那么此時,使用EXISTS的效率,是要大于IN的。這是因為EXISTS每次查詢會循環表A中的數據,然后傳入表B進行匹配,此種情況下,使用EXISTS循環次數少,需要查詢的記錄條目也更少。
2、反之,如果子查詢外部的表A,數量大于子查詢內部的表B,那么此時,使用IN的效率會更高。此時使用IN可以縮小要查找的記錄范圍,最終匹配表A時可以減少記錄的大小。
總結
今天我們闡述了子查詢的概念和一些簡單的用法,以及一些優化方式。我們可以將子查詢分為關聯子查詢和非關聯子查詢兩種模式,分別對應IN和EXISTS的用法。最后我們在優化子查詢時提到了使用哪種方式取決于外表和內表的大小關系,其實優化的重心在于使用小表來驅動大表的原則,這些你都學會了嗎?
下一期我們會從數據庫索引的角度來繼續講述數據庫性能優化的方式,朋友們,我們下期再見!
您的點贊和在看是我創作的最大動力,感謝支持
公眾號:wacky的碎碎念
知乎:wacky