SQL(2)
介紹多表查詢等複雜SQL語句。
關係資料庫的查詢結果都是一個結果表(也是關係)
集聚函式
基本語法
- 統計元組個數
- COUNT(*)
- 統計一列中值的個數
- COUNT([DISTINCT|ALL]<列名>)
- 計算一列值的總和(此列必須為數值型)
- SUM([DISTINCT|ALL]<列名>)
- 計算一列值的平均值(此列必須為數值型)
- AVG([DISTINCT|ALL]<列名>)
- 求一列中的最大值和最小值
- MAX([DISTINCT|ALL]<列名>)
- MIN([DISTINCT|ALL]<列名>)
例子
-
查詢選修1號課程的學生最高分數
-
SELECTMAX(Grade) FROM SC WHERE Cno='1';
-
-
查詢學生201215012選修課程的總學分數
-
SELECT SUM(Ccredit) FROM SC,Course WHERE Sno='201215012' AND
-
GROUP BY 子句
細化聚集函式的作用物件
- 如果未對查詢結果分組,聚集函式將作用於整個查詢結果
- 對查詢結果分組後,聚集函式將分別作用於每個組
- 按指定的一列或多列值分組,值相等的為一組
HAVING
短語與WHERE
子句的區別:
- 作用物件不同
WHERE
子句作用於基表或檢視,從中選擇滿足條件的元組HAVING
短語作用於組,從中選擇滿足條件的組WHERE
子句不能使用聚合函式!
例子
-
求各個課程號及相應的選課人數
SELECT Cno,
-
查詢選修了3門以上課程的學生學號
SELECT Sno FROM SC GROUP BY Sno HAVING COUNT(*) >3;
-
查詢平均成績大於等於90分的學生學號和平均成績
SELECT Sno, AVG(Grade) FROM SC GROUP BY Sno HAVING AVG(Grade)>=90;
這裡只能使用
HAVING
,不能使用WHERE
。
ORDER BY子句
- 可以按一個或多個屬性列排序
- 優先順序逐漸降低
- 升序:ASC;
- 降序:DESC;
- 預設值為升序
- 對於空值,排序時顯示的次序由具體系統實現來決定
例子
-
查詢選修了3號課程的學生的學號及其成績,查詢結果按分數降序排列
SELECT Sno, Grade FROM SC WHERE Cno= ' 3 ' ORDER BY Grade DESC;
-
查詢全體學生情況,查詢結果按所在系的系號升序排列,同一系中的學生按年齡降序排列
SELECT * FROM Student ORDER BY Sdept, Sage DESC;
連線查詢
-
連線查詢:同時涉及兩個以上的表的查詢
-
連線條件或連線謂詞:用來連線兩個表的條件
-
一般格式:
[<表名1>.]<列名1> <比較運算子> [<表名2>.]<列名2>
[<表名1>.]<列名1>BETWEEN [<表名2>.]<列名2>AND[<表名2>.]<列名3>
-
連線欄位:連線謂詞中的列名稱
- 連線條件中的各連線欄位型別必須是可比的,但名字不必相同
(非)等值連線查詢
等值連線:連線運算子為=
,這裡與Join
操作等價。
例子
-
查詢每個學生及其選修課程的情況
SELECT Student.*, SC.* FROM Student, SC WHERE Student.Sno = SC.Sno;
-
一條SQL語句可以同時完成選擇和連線查詢,這時WHERE子句是由連線謂詞和選擇謂片語成的複合條件。
查詢選修2號課程且成績在90分以上的所有學生的學號和姓名
SELECT Student.Sno, Sname FROM Student, SC WHERE Student.Sno=SC.Sno AND SC.Cno=' 2 ' AND SC.Grade>90;
執行過程
巢狀迴圈法(NESTED-LOOP)
- 首先在表1中找到第一個元組,然後從頭開始掃描表2,逐一查詢滿足連線件的元組,找到後就將表1中的第一個元組與該元組拼接起來,形成結果表中一個元組。
- 表2全部查詢完後,再找表1中第二個元組,然後再從頭開始掃描表2,逐一查詢滿足連線條件的元組,找到後就將表1中的第二個元組與該元組拼接起來,形成結果表中一個元組。
- 重複上述操作,直到表1中的全部元組都處理完畢
可以發現,等值連線的複雜度很高,為O(m* n)。
自身連線
- 自身連線:一個表與其自己進行連線
- 需要給表起別名以示區別
- 由於所有屬性名都是同名屬性,因此必須使用別名字首
例子
-
查詢每一門課的間接先修課(即先修課的先修課)
SELECT FIRST.Cno, SECOND.Cpno FROM Course FIRST, Course SECOND WHERE FIRST.Cpno = SECOND.Cno;
外連線
外連線與普通連線的區別
普通連線
操作只輸出滿足連線條件的元組外連線
操作以指定表為連線主體,將主體表中不滿足連線條件的元組一併輸出左外連線
- 列出左邊關係中所有的元組
右外連線
- 列出右邊關係中所有的元組
例子
SELECT Student.Sno, Sname, Ssex, Sage, Sdept, Cno, Grade
FROM Student LEFT OUT JOIN SC ON
(Student.Sno=SC.Sno);
多表連線
兩個以上的表進行連線。
MongoDB不提供這種操作:
JOIN
很慢- 多級擴充套件能力差,代價太高
例子
-
查詢每個學生的學號、姓名、選修的課程名及成績
SELECT Student.Sno, Sname, Cname, Grade FROM Student, SC, Course /*多表連線*/ WHERE Student.Sno = SC.Sno AND SC.Cno = Course.Cno;
巢狀查詢
-
一個
SELECT-FROM-WHERE
語句稱為一個查詢塊 -
將一個查詢塊巢狀在另一個查詢塊的
WHERE
子句或HAVING
短語的條件中的查詢稱為巢狀查詢 -
上層的查詢塊稱為外層查詢或父查詢
-
下層查詢塊稱為內層查詢或子查詢
-
SQL語言允許多層巢狀查詢
- 即一個子查詢中還可以巢狀其他子查詢
-
子查詢的限制
- 不能使用ORDERBY子句
- 因為ORDER BY 結果為有序的,不滿足關係的定義,只能作為最後的生成結果
帶有IN謂詞的子查詢
-
查詢與“劉晨”在同一個系學習的學聲
SELECT Sno, Sname, Sdept FROM Student WHERE Sdept IN (SELECT Sdept FROM Student WHERE Sname= ' 劉晨 '); /*用自身連線表示*/ SELECT S1.Sno, S1.Sname,S1.Sdept FROM Student S1,Student S2 WHERE S1.Sdept = S2.Sdept AND S2.Sname = '劉晨';
-
查詢選修了課程名為“資訊系統”的學生學號和姓名
SELECT Sno,Sname FROM Student WHERE Sno IN (SELECT Sno FROM SC WHERE Cno IN (SELECT Cno FROM Course WHERE Cname= '資訊系統' ) ); /*用連線查詢表示*/ SELECT Sno,Sname FROM Student,SC,Course WHERE Student.Sno = SC.Sno AND SC.Cno = Course.Cno AND Course.Cname='資訊系統';
帶有比較運算子的子查詢
-
當能確切知道內層查詢返回單值時,可用比較運算子
(>,<,=,>=,<=,!=或< >)
。 -
由於一個學生只可能在一個系學習,則可以用 = 代替IN :
SELECT Sno,Sname,Sdept FROM Student WHERE Sdept = (SELECT Sdept FROM Student WHERE Sname= '劉晨');
-
注意,用比較運算子取巢狀,只能
SELECT
一個屬性,且為數值型別。 -
不相關子查詢
- 子查詢的查詢條件不依賴於父查詢
- 由裡向外逐層處理。即每個子查詢在上一級查詢處理之前求解,子查詢的結果用於建立其父查詢的查詢條件。
-
相關子查詢
- 子查詢的查詢條件依賴於父查詢
- 首先取外層查詢中表的第一個元組,根據它與內層查詢相關的屬性值處理內層查詢,若
WHERE
子句返回值為真,則取此元組放入結果表 - 然後再取外層表的下一個元組
- 重複這一過程,直至外層表全部檢查完為止
例子
-
找出每個學生超過他選修課程平均成績的課程號
SELECT Sno, Cno FROM SC x WHERE Grade >= ( SELECT AVG(Grade) FROM SC y WHERE y.Sno = x.Sno ); /*用連線查詢表示*/ SELECT First.Sno, First.Cno FROM SC First JOIN ( SELECT Sno, AVG(Grade) as A_Grade FROM SC GROUP BY Sno) SA ON First.Sno = SA.Sno WHERE First.Grade > SA.A_Grade
帶有ANY(SOME)或ALL謂詞的子查詢
使用ANY或ALL謂詞時必須同時使用比較運算
若子查詢中不是唯一的,使用ANY/ALL可以使用比較運算子
語義為:
> ANY 大於子查詢結果中的某個值
>ALL 大於子查詢結果中的所有值
>=ANY 大於等於子查詢結果中的某個值
<=ANY 小於等於子查詢結果中的某個值
=ANY 等於子查詢結果中的某個值
!=(或<>)ALL 不等於子查詢結果中的任何一個值
例子
-
查詢非計算機科學系中比計算機科學系任意一個學生年齡小的學生姓名和年齡
SELECT Sname,Sage FROM Student WHERE Sage < ANY ( SELECT Sage FROM Student WHERE Sdept= ' CS ') AND Sdept <> ‘CS ' ; /*父查詢塊中的條件 */ /*用聚集函式實現*/ SELECT Sname,Sage FROM Student WHERE Sage < ( SELECT MAX(Sage) FROM Student WHERE Sdept= 'CS ') AND Sdept <> ' CS ';
-
查詢非計算機科學系中比計算機科學系所有學生年齡都小的學生姓名及年齡
SELECT Sname,Sage FROM Student WHERE Sage < ALL (SELECT Sage FROM Student WHERE Sdept= ' CS ') AND Sdept <> ' CS ’; /*用聚集函式實現*/ SELECT Sname,Sage FROM Student WHERE Sage < (SELECT MIN(Sage) FROM Student WHERE Sdept= ' CS ') AND Sdept <>' CS ';
帶有EXISTS謂詞的子查詢
EXISTS謂詞
- 存在量詞
- 帶有EXISTS謂詞的子查詢不返回任何資料,只產生邏輯真值“true”或邏輯假值“false”。
- 若內層查詢結果非空,則外層的WHERE子句返回真值
- 若內層查詢結果為空,則外層的WHERE子句返回假值
- 由EXISTS引出的子查詢,其目標列表達式通常都用* ,因為帶EXISTS的子查詢只返回真值或假值,給出列名無實際意義。
例子
-
查詢所有選修了1號課程的學生姓名。
思路
- 本查詢涉及
Student
和SC
關係 - 在
Student
中依次取每個元組的Sno
值,用此值去檢查SC
表 - 若
SC
中存在這樣的元組,其Sno
值等於此Student.Sno
值,並且其Cno=‘1’
,則取此Student.Sname
送入結果表
SELECT Sname FROM Student WHERE EXISTS (SELECT * FROM SC WHERE Sno=Student.Sno AND Cno= ' 1 ');
- 本查詢涉及
-
查詢沒有選修1號課程的學生姓名。
SELECT Sname FROM Student WHERE NOT EXISTS (SELECT * FROM SC WHERE Sno = Student.Sno AND Cno='1');
難點
-
不同形式的查詢間的替換
-
一些帶EXISTS或NOT EXISTS謂詞的子查詢不能被其他形式的子查詢等價替換
-
所有帶IN謂詞、比較運算子、ANY和ALL謂詞的子查詢都能用帶EXISTS謂詞的子查詢等價替換
-
查詢與“劉晨”在同一個系學習的學生
-
可以用帶EXISTS謂詞的子查詢替換
-
SELECT Sno,Sname,Sdept FROM Student S1 WHERE EXISTS (SELECT * FROM Student S2 WHERE S2.Sdept = S1.Sdept AND S2.Sname = '劉晨');
-
-
-
用EXISTS/NOT EXISTS實現全稱量詞(難點)
-
查詢選修了全部課程的學生姓名
-
不存在一門課,這個學生沒有選
-
SELECT Sname FROM Student WHERE NOT EXISTS (SELECT * FROM Course WHERE NOT EXISTS (SELECT * FROM SC WHERE Sno= Student.Sno AND Cno= Course.Cno ) );
-
-