SQL是一套標準,全稱結構化查詢語言,是用來完成和數(shù)據(jù)庫之間的通信的編程語言,SQL語言是腳本語言,直接運行在數(shù)據(jù)庫上。同時,SQL語句與數(shù)據(jù)在數(shù)據(jù)庫上的存儲方式無關,只是不同的數(shù)據(jù)庫對于同一條SQL語句的底層實現(xiàn)不同罷了,但結果相同。這有點類似于java中接口的作用,一個接口可以有不同的實現(xiàn)類,不同的實現(xiàn)類對于接口中方法的實現(xiàn)方式可以不同,結果可以相同。這里SQL語言的作用就類似于java中的接口,數(shù)據(jù)庫就類似于java中接口的實現(xiàn)類,SQL語句就類似于java接口中的方法。不同的是java中接口的不同實現(xiàn)類對于接口中方法的執(zhí)行結果可以相同,也可以不同,而不同的數(shù)據(jù)庫對于同一條SQL語句的執(zhí)行是相同的。(這里只是做一個類比,方便我們理解)
一般情況下,大部分SQL語句在不同的數(shù)據(jù)庫上是通用的,但我們知道每個數(shù)據(jù)庫都有自己獨有的特性,像在MySql數(shù)據(jù)庫中,可以使用substr(取字符串),trim(去空格),ifnull(空值處理函數(shù)),還可以使用limit語句對數(shù)據(jù)庫表進行截取,但這些都是oracle數(shù)據(jù)庫沒有的。(類比接口實現(xiàn)類中,實現(xiàn)類獨有的方法,而接口中沒有的)
這里簡單介紹一下mysql數(shù)據(jù)庫,mysql數(shù)據(jù)庫是一款關系型數(shù)據(jù)庫,所謂關系型數(shù)據(jù)庫就是以二維表的形式存儲數(shù)據(jù),使用行和列方便我們對數(shù)據(jù)的增刪改查。
這篇博客,我們以mysql數(shù)據(jù)庫為例,對一條sql語句的執(zhí)行流程進行分析。(本篇博客不涉及到表連接)
首先,創(chuàng)建一張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;
初始化數(shù)據(jù):
INSERT INTO student(`name`,`subject`,grade)VALUES('aom','語文',88);INSERT INTO student(`name`,`subject`,grade)VALUES('aom','數(shù)學',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','數(shù)學',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','數(shù)學',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','數(shù)學',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','數(shù)學',55);INSERT INTO student(`name`,`subject`,grade)VALUES('rajo','外語',55);
下面我們來看一下,數(shù)據(jù)在數(shù)據(jù)庫中的存儲形式。
?。▓D1.0)
現(xiàn)在針對這張student表中的數(shù)據(jù)提出一個問題:要求查詢出掛科數(shù)目多于兩門(包含兩門)的前兩名學生的姓名,如果掛科數(shù)目相同按學生姓名升序排列。
下面是這條查詢的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;
執(zhí)行結果:
圖(1.1)
以上這條sql語句基本上概括了單表查詢中所有要注意的點,那么我們就以這條sql為例來分析一下一條語句的執(zhí)行流程。
1,一條查詢的sql語句先執(zhí)行的是?FROM student?負責把數(shù)據(jù)庫的表文件加載到內存中去,如圖1.0中所示。(mysql數(shù)據(jù)庫在計算機上也是一個進程,cpu會給該進程分配一塊內存空間,在計算機‘服務’中可以看到,該進程的狀態(tài))
圖(1.2)
2,WHERE grade?<?60,會把(圖1.0)所示表中的數(shù)據(jù)進行過濾,取出符合條件的記錄行,生成一張臨時表,如下圖所示。
圖(1.3)
?
3,GROUP?BY `name`會把圖(1.3)的臨時表切分成若干臨時表,我們用下圖來表示內存中這個切分的過程。
圖(1.4) 圖(1.5) 圖(1.6) ?圖(1.7)
4,SELECT?的執(zhí)行讀取規(guī)則分為sql語句中有無GROUP?BY兩種情況。
(1)當沒有GROUP?BY時,SELECT?會根據(jù)后面的字段名稱對內存中的一張臨時表整列讀取。
?。?)當查詢sql中有GROUP?BY時,會對內存中的若干臨時表分別執(zhí)行SELECT,而且只取各臨時表中的第一條記錄,然后再形成新的臨時表。這就決定了查詢sql使用GROUP?BY的場景下,SELECT后面跟的一般是參與分組的字段和聚合函數(shù),否則查詢出的數(shù)據(jù)要是情況而定。另外聚合函數(shù)中的字段可以是表中的任意字段,需要注意的是聚合函數(shù)會自動忽略空值。
我們還是以本例中的查詢sql來分析,現(xiàn)在內存中有四張被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對上圖所示臨時表中的數(shù)據(jù)再次過濾,與WHERE語句不同的是HAVING?用在GROUP?BY之后,WHERE是對FROM student從數(shù)據(jù)庫表文件加載到內存中的原生數(shù)據(jù)過濾,而HAVING?是對SELECT?語句執(zhí)行之后的臨時表中的數(shù)據(jù)過濾,所以說column AS otherName ,otherName這樣的字段在WHERE后不能使用,但在HAVING?后可以使用。但HAVING的后使用的字段只能是SELECT?后的字段,SELECT后沒有的字段HAVING之后不能使用。HAVING num?>=?2語句執(zhí)行之后生成一張臨時表,如下:
? 圖(1.9)
6,ORDER?BY num?DESC,`name`?ASC對以上的臨時表按照num,name進行排序。
7,LIMIT?0,2取排序后的前兩個。
以上就是一條sql的執(zhí)行過程,同時我們在書寫查詢sql的時候應當遵守以下順序。
SELECT XXX FROM XXX WHERE XXX GROUP BY XXX HAVING XXX ORDER BY XXX LIMIT XXX;
?
最后說一點,我們作為程序員,研究問題還是要仔細深入一點的。當你對原理了解的有夠透徹,開發(fā)起來也就得心應手了,很多開發(fā)中的問題和疑惑也就迎刃而解了,而且在面對其他問題的時候也可做到觸類旁通。當然在開發(fā)中沒有太多的時間讓你去研究原理,開發(fā)中要以實現(xiàn)功能為前提,可等項目上線的后,你有大把的時間或者空余的時間,你大可去刨根問底,深入的去研究一項技術,為覺得這對一名程序員的成長是很重要的事情。
?
來源:http://www.icode9.com/content-2-132151.html