一條SQL語句的千迴百轉

SQL語言相信你們都不陌生,從本質上來講,它是一種結構化查詢語言,是用來數據庫之間的通訊的編程語言。做爲一名Java程序員,咱們從Java角度來看,SQL語言至關於Java接口,而數據庫是實現這個接口的實現類,SQL語句則是實現類的方法!!。從這裏咱們就能夠理解了,每一個數據庫都有着本身獨特的規則,但大致上是遵循SQL標準。html

SQL 語句有一個讓大部分人都感到困惑的地方,就是我寫的 SQL 語句的跟我預想要的結果不同。在這裏,咱們就以 Mysql 數據庫爲例,對一條 SQL 語句的執行順序進行分析。mysql

首先看一下示例語句程序員

image

它的執行順序是這樣的sql

image

準備數據數據庫

咱們會先準備一些數據,即建立 classes、student 表,並插入測試數據, SQL語句以下:編程

DROP TABLE classes;
CREATE TABLE classes (class_id varchar(10), class_name varchar(10)) ENGINE=InnoDB DEFAULT CHARSET=utf8 DEFAULT COLLATE=utf8_general_ci;
INSERT INTO classes (class_id, class_name) VALUES ('2', '2班');
INSERT INTO classes (class_id, class_name) VALUES ('1', '1班');

DROP TABLE student;
CREATE TABLE student (stu_name varchar(10), class_id varchar(10), stu_id varchar(10)) ENGINE=InnoDB DEFAULT CHARSET=utf8 DEFAULT COLLATE=utf8_general_ci;

INSERT INTO student (stu_name, class_id, stu_id) VALUES ('王五', '2', '124');
INSERT INTO student (stu_name, class_id, stu_id) VALUES ('王五', '2', '123');
INSERT INTO student (stu_name, class_id, stu_id) VALUES ('李四', '2', '122');
INSERT INTO student (stu_name, class_id, stu_id) VALUES ('李四', '1', '114');
INSERT INTO student (stu_name, class_id, stu_id) VALUES ('張三', '1', '112');
INSERT INTO student (stu_name, class_id, stu_id) VALUES ('張三', '2', '121');
INSERT INTO student (stu_name, class_id, stu_id) VALUES ('李四', '1', '113');
INSERT INTO student (stu_name, class_id, stu_id) VALUES ('張三', '1', '111');
INSERT INTO student (stu_name, class_id, stu_id) VALUES ('小紅', '', '141');
INSERT INTO student (stu_name, class_id, stu_id) VALUES ('王五', '2', '125');

OK,有了數據以後,咱們就能夠來看看 SQL 語句在 MySQL 中執行過程了,SQL 語句以下:編程語言

select stu_name as name,count(stu_name) total from student s left join classes c on s.class_id=c.class_id where stu_name in('張三','王五','李四') group by name HAVING count(stu_name)>2 order by stu_id desc limit 1;

SQL執行之旅測試

可能你如今還對 Mysql 語句的執行順序只知其一;不知其二,不要緊,下來我將按 SQL 執行的順序詳細介紹每一個關鍵字的做用,以及注意的地方。code

一、FROM:對FROM子句中的前兩個表執行笛卡爾積(交叉聯接),生成虛擬表VT1。htm

image

二、ON:對VT1應用ON篩選器,只有那些使爲真才被插入到VT2。ON不能單獨,在這裏你能夠把ON理解爲WHERE。

三、JOIN:若是指定了OUTER JOIN(相對於CROSS JOIN或INNER JOIN),保留表(主表)中不符合ON條件匹配的行將做爲外部行添加到VT2,生成VT3。若是FROM子句超過兩個表,上一個聯接生成的結果表會和下一個表重複執行步驟1到步驟3,直處處理完全部的表的關聯。

四、WHERE:對VT3應用WHERE篩選器,只有爲true的行才插入VT4。

五、GROUP BY:按GROUP BY子句中的列列表對VT4中的行進行分組,生成VT5。

在這裏會有一個奇怪的現象,MySQL執行順序GROUP BY -> HAVING -> SELECT,從順序看SELECL在GROUP BY以後,GROUP BY 應該不可使用SELECT字段別名,可是在GROUP BY卻可使用SELECT字段別名,主要緣由MySQL擴展了標準SQL,容許GROUP BY子句使用的SELECT子句中的別名以及和非列表表達式等標準, 並認爲語句是有效的。
從MySQL 5.7.5開始,默認SQL mode模式包括 ONLY_FULL_GROUP_BY。(在5.7.5以前,MySQL不檢測功能依賴性,ONLY_FULL_GROUP_BY默認狀況下不啓用。更多請參考:MySQL 5.7 Reference Manual-GROUP BY

六、HAVING :對VT5應用HAVING篩選器,只有爲true的組插入到VT6。

HAVING同GROUP BY同樣,MySQL拓展SQL標準以容許HAVING可使用別名和非列表表達式

七、SELECT:將VT6每一組數據執行select xx,有幾組就執行幾回,產生VT7。

這裏有一點要注意,當SQL mode 模式ONLY_FULL_GROUP_BY不開啓,不會強制SELECT指定的字段必須屬於GROUP BY後的條件。若符合條件的字段有多個,則只顯示第一次出現的字段。雖然這種查詢在語法上經過了,但結果並無什麼意義,由於其餘字段並不是須要的準確值。因此最好select語句指定的字段必須是「分組依據字段」。

八、ORDER BY:將VT7中的行按ORDER BY子句中的列列表順序,ORDER BY只能選擇SELECT的字段

九、LIMIT:從VT7的開始處選擇指定數量或比例的行,生成表VT8,並返回給調用者。

OK,到這裏就執行結束了。咱們能夠發現,SQL 語句的語法順序和執行順序並不一致,若是你已經能夠清醒知道它們之間差別,你就能夠看出爲何之前寫的SQL老是和咱們預想的不一致。你看,哪怕只有一條小小的 SQL 語句都有這麼多門道,只有不斷專研探究,咱們才能夠真正掌握這一門技術。

這裏多提一下,在SQL語法有幾點要特別注意,SELECT雖然在GROUP BY和HAVING 以後,可是若是SQL mode模式 ONLY_FULL_GROUP_BY不開啓,GROUP BY和HAVING是容許使用SELECT的字段,並且也不會強制SELECT指定的字段必須屬於GROUP BY後的條件。至此SQL的解析之旅就結束了,最後用一張圖總結一下今天的內容:

image

參考:MySQL 5.7 Reference Manual

相關文章
相關標籤/搜索