編寫 SQL 語句是每個程序員應該具備的基本功。在實際開發過程中,需要編寫比較復雜的 SQL 查詢語句是必不可少的,但很多 SQL 書籍上不是簡單的介紹一下就是出最終的查詢語句,編寫復雜 SQL 查詢的具體思路卻沒有多少介紹,這不能不說是一種巨大的遺憾 , 看著一串無比復雜的 SQL 語句,沒有掌握方法的話誰看了都會頭暈 ^-^
回憶一下學習編程語言的經歷( C++,java 等),我們一般都是先學習變量的定義,然后是流程控制語句,接著是函數,類等等。但我們在學習 SQL , SQL 書籍上都普遍忽略了一個重要的方面: SQL 語句的執行順序。 不知道是什么原因,這一點確實沒被多少書籍提過。掌握了SQL語句的執行順序的規律,就能較輕松的編寫出復雜的 SQL 查詢。
SQL 語句的執行順序如下:
1 、 from 子句組裝來自不同數據源的數據;
2 、 where 子句基于指定的條件對記錄行進行篩選;
3 、 group by 子句將數據劃分為多個分組;
4 、使用聚集函數進行計算;
5 、使用 having 子句篩選分組;
6 、計算所有的表達式;
7 、使用 order by 對結果集進行排序。
下面舉一個簡單的例子舉例說明,假設有以下一張表 student :
ID
Name
Age
1
Tom
23
2
Jack
25
3
Lucy
15
4
Anay
18
5
Bobby
21
要求通過 SQL 語句把年齡大于 20 的學生姓名查出來
SQL 語句如下:
Select name
From student
Where age>20
結果是:
Tom
Jack
Bobby
那對于這個簡單的 SQL 語句,執行順序是怎么樣的呢?
1. from 子句組裝來自不同數據源的數據,簡單點來說就是要確定查詢的數據來自哪個表。如果 from 關鍵字后跟的表有兩個或以上,就產生笛卡爾積。
2. where 子句對每個記錄行進行 篩選,把不符合條件的行篩選掉。
3. 針對符合條件的行執行相應的表達式操作,即 select 部分。
我們針對前面的寫的 SQL 語句簡單模擬一下執行過程:
1. 確定數據表,我們能根據 from 子句( From student )確定數據是來自下面的的表 student
ID
Name
Age
1
Tom
23
2
Jack
25
3
Lucy
15
4
Anay
18
5
Bobby
21
表 1
2. 根據 where 子句中的條件( Where age>20 )篩選 記錄行,請留意, where 子句的 篩選是對每一行 from 表中的每一行進行的。
(1) 對于第 1 行
1
Tom
23
Age=23>20, 符合條件
(2) 對于第 2 行
2
Jack
25
Age=25>20, 符合條件
(3) 對于第 3 行
3
Lucy
15
Age=15<20, 不符合條件
(4) 對于第 4 行
4
Anay
18
Age=18<20, 不符合條件
(5) 對于第 5 行
5
Bobby
21
Age=21>20, 符合條件
由上述的 (1)(2)(5) 可知,最終符合條件的記錄為下表 2
ID
Name
Age
1
Tom
23
2
Jack
25
5
Bobby
21
表 2
4. 計算所有的表達式,即 Select name 部分,針對表 2 中的數據,最終符合條件的是 3 行,分別從每一行挑選出需要的字段值 name ,最終的結果如下表 3
Name
Tom
Jack
Bobby
表 3
下面舉一般比較復雜的例子,有 3 個表 teacher 表, student 表, tea_stu 關系表:
teacher 表 teaID name age
student 表 stuID name age
teacher_student 表 teaID stuID
要求用一條 sql 查詢出這樣的結果
1. 顯示的字段要有老師 name, 每個老師所帶的學生人數
2 只列出老師 age 為 45 以下,學生 age 為 12 以上的記錄
先準備測試數據:
drop table if exists tea_stu;
drop table if exists teacher;
drop table if exists student;
create table teacher(teaID int PRimary key,name varchar(50),age int);
create table student(stuID int primary key,name varchar(50),age int);
create table tea_stu(teaID int references teacher(teaID),stuID int references student(stuID));
insert into teacher values(1,' Tom',46), (2,' Jack',35) , (3,' Tony',36) , (4,' Lucy',37);
insert into student values(1,' Lili',11), (2,' Anay',15) , (3, 'Bobby',16) , (4, 'Jeff',17);
insert into tea_stu values(1,1), (1,2), (1,3),(2,2), (2,3), (2,4),(3,3), (3,4), (3,1),(4,4), (4,1), (4,2) , (4,3);
題目要求是列出 老師所帶的學生數,條件是 老師 age 為 45 以下,學生 age 為 12 以上,最理想的情況是有下面的一個表 , 如圖 1
圖 1
如果能構造一個圖 1 的表,那么實現題目要求的 SQL 語句用下面的簡單 SQL 查詢就行:
select teacher.name, count(student.name)
from table
where teacher.age<45
and student.age>12
group by teacher.name;
數據庫中學生的信息和老師的信息是分別存放在 student, teacher 表中的,信息的關聯只能依靠 tea_stu ,那么怎么構造圖 1 的表呢?這時候可以用到表的關聯,把這三個表的數據關聯起來, 注意:只要是表的關聯就會產生笛卡爾積,所以務必把笛卡爾積去掉。 關聯表的最小粒度關聯可以 去掉 笛卡爾積,具體的查詢語句為:
select teacher.name, teacher.age,student.name,student.age
from teacher,student,tea_stu
where teacher.teaID=tea_stu.teaID
and student.stuID=tea_stu.stuID
所以綜合以上所述,就能得出最終的查詢語句
select teacher.name, count(student.name) student_num
from teacher,student,tea_stu
where teacher.teaID=tea_stu.teaID
and student.stuID=tea_stu.stuID
and teacher.age<45
and student.age>12
group by teacher.name;
結果如圖 2 所示:
圖 2