資料庫系統概論學習筆記(四):SQL的簡單應用之資料查詢
嗯……最實用的部分,其他都可以不會,唯獨這個必須熟練吧 —— 資料查詢
這篇筆記主要是例子,使用的例子是《筆記(三)》中定義的學生選課關係。使用的SQL語句可能與教材上有些出入,因為我使用的是MySQL 5.7,對SQL語言支援可能會有些許差異。
SELECT語句的一般格式
SELECT [ ALL | DISTINCT ] < 目標列表達式 > [ 別名 ],…
FROM < 表名或檢視名 > [ 別名 ],…
[ WHERE < 條件表示式 > ]
[ GROUP BY < 列名 > [ HAVING < 條件表示式 > ] ]
[ ORDER BY
單表查詢
選擇表中的若干列
1. 查詢指定列
例1、查詢全體學生的學號與姓名。
SELECT Sno,Sname
FROM Student;
2. 查詢全部列
例2、查詢全體學生的詳細記錄。
SELECT *
FROM Student;
3. 查詢經過計算的值
例3、查詢全體學生的姓名及出生年份。
SELECT Sname, 2017-Sage Birthyear
FROM Student;
例4、查詢全體學生的姓名、出生年份和所在院系,並要求用小寫字母表示所有系名。
SELECT Sname,"Year of Birth",2017-Sage,LOWER(Sdept)
FROM Student;
選擇表中的若干元組
1. 消除取值重複的行
例5、查詢選修了課程的學生學號。
SELECT DISTINCT(Sno)
FROM SC;
2.查詢滿足條件的元組
比較大小:“=”,“>”,“>=”,“<”,“<=”,“!= 或 <>”
例6、查詢計算機科學系全體學生的名單。
SELECT Sname
FROM Student
WHERE Sdept = 'CS';
確定範圍
例7、查詢年齡在20~23歲(包括20歲和23歲)之間的學生的姓名、系別和年齡。
SELECT Sname,Sdept,Sage
FROM Student
WHERE Sage BETWEEN 20 AND 23;
確定集合:“[ NOT ] IN”
例8、查詢計算機科學系(CS)、數學系(MA)和資訊系(IS)學生的姓名和性別。
SELECT Sname,Ssex
FROM Student
WHERE Sdept IN ('CS','MA','IS');
字元匹配:“[ NOT ] LIKE ‘<匹配串>’ [ ESCAPE ‘<換碼字元>’]”
“%”表示任意長度的字串。
“_”(下劃線),表示任意的單個字元。
例9、查詢所有姓劉的學生的姓名、學號和性別。
SELECT Sname,Sno,Ssex
FROM Student
WHERE Sname LIKE '劉%';
例10、查詢名字中第二個字為‘晨’,且名字長度為兩個字的學生的詳細資訊。
SELECT *
FROM Student
WHERE Sname LIKE '_晨'; /* 我的MySQL使用的字符集是utf8,所以一個漢字也是一格 */
例11、查詢以“DB_”開頭,倒數第3個字元為i的課程資訊。
SELECT *
FROM Course
WHERE Cname LIKE 'DB/_%i__' ESCAPE '/';
ESCAPE ‘/’:表示符號’/’為轉義字元,其後所接的字元不具有萬用字元的作用。
涉及空值的查詢:“IS [ NOT ] NULL”
例12、查詢沒有先行課要求的課程名及學分。
SELECT Cname,Ccredit
FROM Course
WHERE Cpno IS NULL;
多重條件查詢:“AND”,“OR”,“NOT”
例13、查詢計算機科學系年齡在20歲以下的學生姓名。
SELECT Sname
FROM Student
WHERE Sdept = 'CS' AND Sage < 20;
ORDER BY 子句
對查詢結果按照一個或多個屬性列的升序(ASC)或降序(DESC)排列,預設值為升序。
例14、查詢選修了3號課程的學生的學號及其成績,查詢結果按分數降序排序。
SELECT Sno,Grade
FROM SC
WHERE Cno = '3'
ORDER BY Grade DESC;
聚集函式(Aggregate Functions)
常用函式及功能:
COUNT( [ DISTINCT | ALL ] * ):統計元組個數
COUNT( [ DISTINCT | ALL ] <列名> ):統計一列值的個數
SUM( [ DISTINCT | ALL ] <列名> ):計算一列值的總和
AVG( [ DISTINCT | ALL ] <列名> ):計算一列值的平均數
MAX( [ DISTINCT | ALL ] <列名> ):求一列值中的最大值
MIN( [ DISTINCT | ALL ] <列名> ):求一列值中的最小值
例15、查詢選修了課程的學生人數。
SELECT COUNT(DISTINCT Sno)
FROM SC;
GROUP BY 子句
將查詢結果按某一列或佇列的值分組,值相等的為一組。通常是為了細化聚集函式的所用範圍,當查詢結果分組後,聚集函式將作用於每一分組,即每個分組有一個聚集函式對應的計算結果。
例16、求各課程號及相應的選課人數。
SELECT Cno,COUNT(Sno)
FROM SC
GROUP BY Cno;
若需要對分組後的結果進行進一步篩選,那麼就可以使用“HAVING”。
例17、查詢有至少兩個學生選修的課程號,以及選課人數。
SELECT Cno,COUNT(Sno)
FROM SC
GROUP BY Cno
HAVING COUNT(Sno) >= 2;
注:“WHERE”作用於基本表或試圖,而“HAVING”作用於分組。
連線查詢
針對兩張或兩張以上表的查詢稱為連線查詢。
等值與非等值連線查詢
用“=”進行連線操作稱為等值連線,用其他運算子時稱為非等值連線。
例18、查詢每個學生及其選修課情況。
SELECT Student.*,SC.*
FROM Student,SC
WHERE Student.Sno = SC.Sno;
自身連線
自己連自己,一種自嗨的連線。
例19、查詢每一門課程的間接先修課(即先修課的先修課)。
SELECT c1.Cno ID,c2.Cpno PPno
FROM Course c1,Course c2 /* 需要取兩個別名 */
WHERE c1.Cpno = c2.Cno;
外連線
不滿足連線條件的元組也顯示出來,並且在對應位置填空值。
例20、查詢每個學生的選修課情況。
SELECT Student.Sno,Sname,Ssex,Sage,Sdept,Cno,Grade
FROM Student LEFT JOIN (SC) ON (Student.Sno = SC.Sno);
當幾張表用於連線的屬性名相同時可以使用“USING”。
The USING(column_list) clause names a list of columns that must exist in both tables.
SELECT Student.Sno,Sname,Ssex,Sage,Sdept,Cno,Grade
FROM Student LEFT JOIN (SC) USING (Sno);
複合條件連線
例21、查詢每個學生的學號、姓名、選修的課程名及成績。
SELECT Student.Sno,Sname,Cname,Grade
FROM Student,Course,SC
WHERE Student.Sno = SC.Sno AND SC.Cno = Course.Cno;
巢狀查詢
注:子查詢中不能使用“ORDER BY”
帶有 IN 謂詞的子查詢
例22、查詢與“劉晨”在同一個系學習的學生。
最容易想到的查詢步驟是分兩步,第一步查詢“劉晨”所在的系 D,第二步查詢在系 D 就讀的所有學生。用巢狀子查詢可以很好的描述這個查詢步驟。
SELECT Sno,Sname,Sdept
FROM Student
WHERE Sdept IN (
SELECT Sdept
FROM Student
WHERE Sname = '劉晨');
例23、查詢選修了課程名為“資訊系統”的學生學號和姓名。
實現這個查詢,必須連線3張表。查詢步驟:第一步,連線Course和SC兩場表,得到選修了“資訊系統”的學生學號集合 S。第二步,查詢Student中所有學號在集合 S 中的學生學號和姓名。
SELECT Sno,Sname
FROM Student
WHERE Sno IN (
SELECT Sno
FROM SC,Course
WHERE SC.Cno = Course.Cno AND Cname = '資訊系統');
帶有比較運算子的子查詢
當我們明確知道子查詢的結果是一個單值的時候可以直接用比較運算子進行連線,這類情況比較簡單和前面兩個例子是一樣的思路。
這裡記錄一下相關子查詢的例子。子查詢的條件依賴父查詢時,就叫相關子查詢。
例24、找出每一個學生超過他選修課程平均成績的課程號。
乍一看,第一步求出每個學生的平均成績,第二步找出他們大於其平均成績的課程號就可以了。但是在這兩部之間就有個問題了,我們怎麼把第二步的學生學號傳到第一步裡面從而求得對應學生的平均成績咧?
相關子查詢的語句如下:
SELECT Sno,Cno
FROM SC x
WHERE Grade >= (
SELECT AVG(Grade)
FROM SC y
WHERE y.Sno = x.Sno );
語句中的“x”和“y”既是表SC的別名,也是元組變數可以表示SC表的一個元組。這條語句的可能執行過程是:
①、從父查詢中取一個元組x,將該元組的Sno值傳入子查詢,以 x.Sno = ‘200215121’為例。子查詢執行如下語句:
SELECT AVG(Grade)
FROM SC y
WHERE y.Sno = '200215121';
②、得到的平均值為88.33333,外層再以此值進行判斷:
SELECT Sno,Cno
FROM SC x
WHERE Grade >= 88.3333; /* x.Sno = '200215121' */
③、查詢得到 ( ‘200215121’ , 1 )。
然後x繼續取下一個元組進行上述查詢步驟,知道父查詢取完所有元組為止。
帶有 ANY(SOME)或 ALL 謂詞的子查詢
使用ANY或ALL謂詞時必須同時使用比較運算子。
例25、查詢其他系中比計算機系某一學生年齡小的學生姓名和年齡。
查詢步驟:第一步,查詢計算機系所有學生的年齡集合 S 。 第二步,查詢其他系年齡比集合 S 中某一個值小的學生。
SELECT Sname,Sage
FROM Student
WHERE Sdept <> 'CS' AND Sage < ANY(
SELECT Sage
FROM Student
WHERE Sdept = 'CS' );
注:使用聚集函式實現此類功能時的執行效率更高。
帶有 EXISTS 謂詞的子查詢
EXISTS 不返回任何資料,只產生邏輯真“true”或邏輯假“false”。所以,子查詢的目標列表達式一般用“*”,因為選擇目標列不影響返回的結果。
例26、查詢所有選修了1號課程的學生姓名。
SELECT Sname
FROM Student
WHERE EXISTS (
SELECT *
FROM SC
WHERE Cno = '1' AND SC.Sno = Student.Sno );
可能的查詢執行方式參考前文的相關子查詢。
另外,教材上還提到了 謂詞演算,之後專門學一下好了(其實是懶,這次不想繼續寫)。
集合查詢
嗯…MySQL貌似只有UNION這一個相關操作。
Selected columns listed in corresponding positions of each SELECT statement should have the same data type.
參加UNION操作的各查詢結果的列數必須相同,對應項的資料型別也必須相同。
至於教材上提到的INTERSECT、EXCEPT等操作用前面的一些功能也能做到。
例27、查詢計算機科學系的學生及年齡不大於19歲的學生。
SELECT *
FROM Student
WHERE Sdept = 'CS'
INTERSECT
SELECT *
FROM Student
WHERE Sage <= 19;