SQL是一套標準,全稱結構化查詢語言,是用來完成和數據庫之間的通訊的編程語言,SQL語言是腳本語言,直接運行在數據庫上。同時,SQL語句與數據在數據庫上的存儲方式無關,只是不一樣的數據庫對於同一條SQL語句的底層實現不一樣罷了,但結果相同。這有點相似於java中接口的做用,一個接口能夠有不一樣的實現類,不一樣的實現類對於接口中方法的實現方式能夠不一樣,結果能夠相同。這裏SQL語言的做用就相似於java中的接口,數據庫就相似於java中接口的實現類,SQL語句就相似於java接口中的方法。不一樣的是java中接口的不一樣實現類對於接口中方法的執行結果能夠相同,也能夠不一樣,而不一樣的數據庫對於同一條SQL語句的執行是相同的。(這裏只是作一個類比,方便咱們理解)java
通常狀況下,大部分SQL語句在不一樣的數據庫上是通用的,但咱們知道每一個數據庫都有本身獨有的特性,像在MySql數據庫中,可使用substr(取字符串),trim(去空格),ifnull(空值處理函數),還可使用limit語句對數據庫表進行截取,但這些都是oracle數據庫沒有的。(類比接口實現類中,實現類獨有的方法,而接口中沒有的)mysql
這裏簡單介紹一下mysql數據庫,mysql數據庫是一款關係型數據庫,所謂關係型數據庫就是以二維表的形式存儲數據,使用行和列方便咱們對數據的增刪改查。程序員
這篇博客,咱們以mysql數據庫爲例,對一條sql語句的執行流程進行分析。(本篇博客不涉及到錶鏈接)sql
首先,建立一張student表,字段有自增主鍵id,學生姓名name,學科subject,成績grade數據庫
建表語句:編程
DROP TABLE IF EXISTS student; CREATE TABLE `student` ( `id` int(5) NOT NULL AUTO_INCREMENT, `name` varchar(10) DEFAULT NULL, `subject` varchar(10) DEFAULT NULL, `grade` double(4,1) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=40 DEFAULT CHARSET=utf8;
初始化數據:oracle
INSERT INTO student(`name`,`subject`,grade)VALUES('aom','語文',88); INSERT INTO student(`name`,`subject`,grade)VALUES('aom','數學',99); INSERT INTO student(`name`,`subject`,grade)VALUES('aom','外語',55); INSERT INTO student(`name`,`subject`,grade)VALUES('jack','語文',67); INSERT INTO student(`name`,`subject`,grade)VALUES('jack','數學',44); INSERT INTO student(`name`,`subject`,grade)VALUES('jack','外語',55); INSERT INTO student(`name`,`subject`,grade)VALUES('susan','語文',56); INSERT INTO student(`name`,`subject`,grade)VALUES('susan','數學',35); INSERT INTO student(`name`,`subject`,grade)VALUES('susan','外語',77); INSERT INTO student(`name`,`subject`,grade)VALUES('alice','語文',88); INSERT INTO student(`name`,`subject`,grade)VALUES('alice','數學',77); INSERT INTO student(`name`,`subject`,grade)VALUES('alice','外語',100); INSERT INTO student(`name`,`subject`,grade)VALUES('rajo','語文',33); INSERT INTO student(`name`,`subject`,grade)VALUES('rajo','數學',55); INSERT INTO student(`name`,`subject`,grade)VALUES('rajo','外語',55);
下面咱們來看一下,數據在數據庫中的存儲形式。編程語言
(圖1.0)函數
如今針對這張student表中的數據提出一個問題:要求查詢出掛科數目多於兩門(包含兩門)的前兩名學生的姓名,若是掛科數目相同按學生姓名升序排列。spa
下面是這條查詢的sql語句
SELECT `name`,COUNT(`name`) AS num FROM student WHERE grade < 60 GROUP BY `name` HAVING num >= 2 ORDER BY num DESC,`name` ASC LIMIT 0,2;
執行結果:
圖(1.1)
以上這條sql語句基本上歸納了單表查詢中全部要注意的點,那麼咱們就以這條sql爲例來分析一下一條語句的執行流程。
1,一條查詢的sql語句先執行的是 FROM student 負責把數據庫的表文件加載到內存中去,如圖1.0中所示。(mysql數據庫在計算機上也是一個進程,cpu會給該進程分配一塊內存空間,在計算機‘服務’中能夠看到,該進程的狀態)
圖(1.2)
2,WHERE grade < 60,會把(圖1.0)所示表中的數據進行過濾,取出符合條件的記錄行,生成一張臨時表,以下圖所示。
圖(1.3)
3,GROUP BY `name`會把圖(1.3)的臨時表切分紅若干臨時表,咱們用下圖來表示內存中這個切分的過程。
圖(1.4) 圖(1.5) 圖(1.6) 圖(1.7)
4,SELECT 的執行讀取規則分爲sql語句中有無GROUP BY兩種狀況。
(1)當沒有GROUP BY時,SELECT 會根據後面的字段名稱對內存中的一張臨時表整列讀取。
(2)當查詢sql中有GROUP BY時,會對內存中的若干臨時表分別執行SELECT,並且只取各臨時表中的第一條記錄,而後再造成新的臨時表。這就決定了查詢sql使用GROUP BY的場景下,SELECT後面跟的通常是參與分組的字段和聚合函數,不然查詢出的數據要是狀況而定。另外聚合函數中的字段能夠是表中的任意字段,須要注意的是聚合函數會自動忽略空值。
咱們仍是以本例中的查詢sql來分析,如今內存中有四張被GROUP BY `name`切分紅的臨時表,咱們分別取名爲 tempTable1,tempTable2,tempTable3,tempTable4分別對應圖(1.4)、圖(1.5)、圖(1.6),圖(1.7)下面寫四條"僞SQL"來講明這個查詢過程。
SELECT `name`,COUNT(`name`) AS num FROM tempTable1; SELECT `name`,COUNT(`name`) AS num FROM tempTable2; SELECT `name`,COUNT(`name`) AS num FROM tempTable3;
SELECT `name`,COUNT(`name`) AS num FROM tempTable4;
最後再次成新的臨時表,以下圖:
圖(1.8)
5,HAVING num >= 2對上圖所示臨時表中的數據再次過濾,與WHERE語句不一樣的是HAVING 用在GROUP BY以後,WHERE是對FROM student從數據庫表文件加載到內存中的原生數據過濾,而HAVING 是對SELECT 語句執行以後的臨時表中的數據過濾,因此說column AS otherName ,otherName這樣的字段在WHERE後不能使用,但在HAVING 後可使用。但HAVING的後使用的字段只能是SELECT 後的字段,SELECT後沒有的字段HAVING以後不能使用。HAVING num >= 2語句執行以後生成一張臨時表,以下:
圖(1.9)
6,ORDER BY num DESC,`name` ASC對以上的臨時表按照num,name進行排序。
7,LIMIT 0,2取排序後的前兩個。
以上就是一條sql的執行過程,同時咱們在書寫查詢sql的時候應當遵照如下順序。
SELECT XXX FROM XXX WHERE XXX GROUP BY XXX HAVING XXX ORDER BY XXX LIMIT XXX;
最後說一點,咱們做爲程序員,研究問題仍是要仔細深刻一點的。當你對原理了解的有夠透徹,開發起來也就駕輕就熟了,不少開發中的問題和疑惑也就迎刃而解了,並且在面對其餘問題的時候也可作到舉一反三。固然在開發中沒有太多的時間讓你去研究原理,開發中要以實現功能爲前提,可等項目上線的後,你有大把的時間或者空餘的時間,你大可去刨根問底,深刻的去研究一項技術,爲以爲這對一名程序員的成長是很重要的事情。