30443資料查詢語言DQL
5.4 SQL的資料查詢功能
資料查詢是資料庫最常用的功能。在關係資料庫中,查詢操作是由SELECT語句來完成。其語法格式如下:
SELECT column_expression FROM table_name | view_name[,table_name | view_name, [,…]] [IN foreign_TABLE] [WHERE... ] [GROUP BY... ] [HAVING... ] [ORDER BY…] [With Owneraccess Option]
其中,column_expression為目標欄位表示式,其語法格式為:
[ALL|DISTINCT|TOP] * | table_name.* | [table_name.]column1_name[[AS] alias_name1]
[, [table_name.] column2_name [ [AS] alias_name2] [, ...]]
SELECT語句的主要作用是,從FROM子句指定的資料表或者檢視中找出滿足WHERE子句設定的條件的元組,並按照SELECT子句指定的目標欄位表示式重新組織這些元組,從而形成新的結果集。如果沒有WHERE子句,則預設選出所有的元組。
以下主要按子句功能來介紹SELECT語句的使用方法,如果不特別說明,則均指基於表5.10所示的資料表student進行查詢。
【例子】 為了能看到下文介紹的SELECT語句的執行效果,在用CREATE TABLE語句建立資料表student以後,請接著在SSMS中執行下列的INSERT語句,以在資料庫中建立與表5.10所示內容完全一樣的資料表:
INSERT student VALUES('20170201','劉洋','女','1997-02-03','計算機應用技術',98.5,'計算機系');
INSERT student VALUES('20170202','王曉珂','女','1997-09-20','計算機軟體與理論',88.1,'計算機系');
INSERT student VALUES('20170203','王偉志','男','1996-12-12','智慧科學與技術',89.8,'智慧技術系');
INSERT student VALUES('20170204','嶽志強','男','1998-06-01','智慧科學與技術',75.8,'智慧技術系');
INSERT student VALUES('20170205','賈簿','男','1994-09-03','計算機軟體與理論',43.0,'計算機系');
INSERT student VALUES('20170206','李思思','女','1996-05-05','計算機應用技術',67.3,'計算機系');
INSERT student VALUES('20170207','蒙恬','男','1995-12-02','大資料技術',78.8,'大資料技術系');
INSERT student VALUES('20170208','張宇','女','1997-03-08','大資料技術',59.3,'大資料技術系');
5.4.1 基本查詢
基本查詢是指基於單表(一個數據表或檢視)的僅僅由SELECT子句和FROM子句構成的SELECT語句。其一般格式如下:
SELECT [ALL|DISTINCT|TOP] * | table_name.* | [table_name.]column1_name[ [AS] alias_name1] [, [table_name.] column2_name [ [AS] alias_name2] [, ...]]
FROM table_name;
由於檢視的查詢與資料表的查詢是一樣的,以下僅考慮資料表的查詢問題。對於檢視的查詢可以由此推廣。
1. 選擇所有欄位
有時候希望查詢結果包含所有的欄位,這時只需將目標欄位表示式設定為星號“*”即可,也可以列出所有的欄位。
【例子】 下列的SELECT語句實現的就是最簡單的基本查詢,其結果將包含所有欄位的全部資料元組:
SELECT * FROM student;
該語句等價於下列的SELECT語句:
SELECT s_no,s_name,s_sex,s_birthday,s_speciality,s_avgrade,s_dept
FROM student;
2. 選擇指定的若干欄位
在很多情況下,使用者僅對錶中的某些欄位感興趣,並且希望這些欄位能夠按照指定的順序列出。
【例子】 查詢全體學生的平均成績和姓名(平均成績在前,姓名在後)。
SELECT s_avgrade,s_name
FROM student;
執行後結果如下:
s_avgrade s_name
------------------------
98.5 劉洋
88.1 王曉珂
89.8 王偉志
75.8 嶽志強
43.0 賈簿
67.3 李思思
78.8 蒙恬
59.3 張宇
每一欄位都是用欄位名來“標識”(欄位名一般為英文),例如,第一欄位標識為“s_avgrade”,第二欄位標識為“s_name”。
這對中國使用者來說並不方便,可以使用帶關鍵子AS的目標欄位表示式來解決,其中AS後面跟由使用者指定的欄位標題(關鍵字AS可以省略)。
【例子】 對於上述問題,可用下列的語句來完成:
SELECT s_avgrade AS 平均成績, s_name AS 姓名 -- AS也可以省略
FROM student;
執行後結果如下:
平均 成績姓名
------------------------
98.5 劉洋
88.1 王曉珂
89.8 王偉志
75.8 嶽志強
43.0 賈簿
67.3 李思思
78.8 蒙恬
59.3 張宇
3. 構造計算欄位
查詢結果中的計算欄位(列)是指根據資料表中的某一個或者若干個欄位進行計算而得到的新欄位,並把它放在查詢結果集中,實際上在資料表中並不存在此欄位。
【例子】 要求查詢全體學生的姓名和年齡。由於資料表student中沒有年齡這一欄位,而僅有與之相關的出生日期(birthday)這一欄位。必須經過出生日期來計算每個學生的年齡,相應的SQL語句如下:
SELECT s_name 姓名, Year(getdate())-Year(s_birthday) 年齡
FROM student;
用到兩個系統函式:getdate()和Year(),它們分別用於獲取datetime型別的系統時間和時間的年份。
上述語句執行的結果如下:
姓名 年齡
-----------------------
劉洋 20
王曉珂 20
王偉志 21
嶽志強 19
賈簿 23
李思思 21
蒙恬 22
張宇 20
5.4.2 帶DISTINCT的查詢
使用SELECT查詢時,SELECT後面可加上下欄位關鍵字,以滿足不同的查詢要求:
ALL:ALL是預設關鍵字,即當不加任何關鍵字時,表示預設使用ALL作為關鍵字。它表示要返回所有滿足條件的元組。前面介紹的查詢正是這種查詢。
TOP:有兩種格式:TOP n和TOP n PERCENT。第一種格式表示返回前面n個元組,而第二種格式則表示返回前面n%個元組;如果n%不是整數,則向上取整。
DISTINCT:如果帶此關鍵字,則在查詢結果中若包含重複記錄(行),則只返回這些重複元組中的一條。即關鍵字DISTINCT保證了查詢結果集中不會包含重複元組,但與DISTINCTROW不一樣的是它不會刪除所有的重複元組。
【例5.6】 查詢表student中涉及的不同的系別資訊。
該查詢要求可用下列的語句完成:
SELECT DISTINCT s_dept 所在的系
FROM student;
執行後結果如下:
所在的系
-----------------
大資料技術系
計算機系
智慧技術系
如果在以上語句中不加上關鍵字DISTINCT,則返回下列結果:
所在的系
-----------------
計算機系
計算機系
智慧技術系
智慧技術系
計算機系
計算機系
大資料技術系
大資料技術系
從以上不難看出關鍵字DISTINCT的作用。如果DISTINCT後面跟有多個欄位名,則DISTINCT必須放在第一欄位名的前面(即緊跟SELECT之後),而不能放在其他欄位名的前面。
【例子】 下列的語句是正確的。
SELECT DISTINCT s_dept, s_sex
FROM student;
而下面的語句則是錯誤的:
SELECT s_dept, DISTINCT s_sex
FROM student;
【例5.7】 查詢表student中前3條記錄,列出它們所有的欄位資訊。
該查詢可用帶關鍵字TOP來實現:
SELECT TOP 3 *
FROM student;
如果用下列語句,雖然8*38% = 3.04,但由於採取向上取整,其結果返回4條記錄:
SELECT TOP 38 PERCENT *
FROM student;
5.4.3 帶WHERE子句的條件查詢
在實際應用中,更多時候是根據一定條件來進行查詢的,即查詢滿足一定條件的部分記錄(而不是表中的所有記錄)。WHERE子句將發揮作用,其一般語法格式如下:
SELECT column_expression FROM table_name WHERE condition_expression
condition_expression是條件表示式,通常稱為查詢條件。查詢條件就是一種邏輯表示式,只有那些使該表示式的邏輯值為真的記錄才按照目標欄位表示式column_expression指定的方式組成一個新記錄而新增到結果集中。
查詢條件是一種邏輯表示式,就可以用一些邏輯聯結聯結詞來構建這種表示式。常用聯結詞包括NOT、OR、AND等,分別表示邏輯意義上的“非”、“或”和“與”。
【例5.8】 要求查詢表student中平均成績為良好(80~90,但不等於90)的學生的學號、姓名、性別和平均成績。
對於這一查詢要求,可用下列的SELECT語句:
SELECT s_no 學號,s_name 姓名,s_sex 性別, s_avgrade 平均成績
FROM student
WHERE s_avgrade>=80 AND s_avgrade<90;
該語句執行後結果如下:
學號 姓名 性別 平均成績
-------------------------------------------------------------
20170202 王曉珂 女 88.1
20170203 王偉志 男 89.8
WHERE子句雖然語法格式比較簡單,但在查詢中卻是使用得最多。下面介紹的查詢大多都會涉及到WHERE子句,讀者應該深刻領會其使用方法。
5.4.4 帶BETWEEN的範圍查詢
有時候需要查詢哪些欄位值在一定範圍內的記錄,這時可以使用帶BETWEEN的查詢語句。其語法格式為:
SELECT column_expression FROM table_name WHERE column_name [NOT] BETWEEN value1 AND value2;
value1和value2都是欄位column_name的具體值,且value1≤value2。該語句的查詢結果是返回所有欄位column_name的值落在value1到value2(包括value1和value2)之間範圍內的記錄。
【例5.9】 要求查詢所有出生在1996年08月01日到1997年10月01日之間(包括這兩個日期)的學生,並將他們的姓名、性別、系別、平均成績以及出生年月列出來。
對於這個查詢要求,可以用下列的語句完成:
SELECT s_name 姓名, s_sex 性別, s_dept 系別, s_avgrade 平均成績,s_birthday 出生年月 FROM student WHERE s_birthday BETWEEN '1996-08-01' AND '1997-10-01’;
執行後結果如下:
姓名 性別 系別 平均成績 出生年月
---------------------------------------------------------------------------------------------------
劉洋 女 計算機系 98.5 1997-02-03
王曉珂 女 計算機系 88.1 1997-09-20
王偉志 男 智慧技術系 89.8 1996-12-12
張宇 女 大資料技術系 59.3 1997-03-08
如果要查詢欄位column_name的值不落在value1到value2(包括不等於value1和value2)之間的所有記錄,則只需在相應的BETWEEN前加上謂詞NOT即可。
例如,上例中查詢不是出生在1996年08月01日到1997年10月01日之間的學生,可用下列的語句:
SELECT s_name 姓名, s_sex 性別, s_dept 系別, s_avgrade 平均成績,s_birthday 出生年月 FROM student WHERE s_birthday NOT BETWEEN '1996-08-01' AND '1997-10-01’;
執行結果如下:
姓名 性別 系別 平均成績 出生年月
------------------------------------------------------------------------------------------------
嶽志強 男 智慧技術系 75.8 1998-06-01
賈簿 男 計算機系 43.0 1994-09-03
李思思 女 計算機系 67.3 1996-05-05
蒙恬 男 大資料技術系 78.8 1995-12-02
BETWEEN只適合於欄位值為數值型的情況。
5.4.5 帶IN的範圍查詢
IN與BETWEEN具有類似的功能,都是查詢滿足欄位值在一定範圍內的記錄。但與BETWEEN不同的是,IN後面必須跟列舉的欄位值表(欄位值的列舉),即把所有的欄位值都列出來,而不能寫為“value1 AND value2”的形式。這相當於在一個集合中進行查詢,適合於那些不是數值型的情況。其語法格式為:
SELECT column_expression FROM table_name WHERE column_name [NOT] IN (value1, value2, …, valuen)
【例5.10】 要求查詢智慧技術系和大資料技術系的學生。
對於這個查詢要求,可以用下列的語句來實現:
SELECT s_no 學號, s_name 姓名, s_sex 性別, s_birthday 出生年月, s_speciality 專業, s_avgrade 平均成績, s_dept 系別 FROM student WHERE s_dept IN ('智慧技術系','大資料技術系')
相應的輸出如下:
學號 姓名 性別 出生年月 專業 平均成績 系別
----------------------------------------------------------------------------------------------------------------------------------------------
20170203 王偉志 男 1996-12-12 智慧科學與技術 89.8 智慧技術系
20170204 嶽志強 男 1998-06-01 智慧科學與技術 75.8 智慧技術系
20170207 蒙恬 男 1995-12-02 大資料技術 78.8 大資料技術系
20170208 張宇 女 1997-03-08 大資料技術 59.3 大資料技術系
實際上,“column_name IN (value1, value2, …, valuen)” 等價“column_name=value1 OR column_name=value2 OR…OR column_name=valuen”。
上例的查詢語句也等價於:
SELECT s_no 學號, s_name 姓名, s_sex 性別, s_birthday 出生年月, s_speciality 專業, s_avgrade 平均成績, s_dept 系別 FROM student WHERE s_dept='智慧技術系' OR s_dept='大資料技術系';
這種IN的語句比帶OR的語句在結構上比較簡潔和直觀。
另外,與BETWEEN類似,對於欄位值不在(value1, value2, …, valuen)中的查詢,可通過在IN之前加上NOT來實現。
5.4.6 帶GROUP的分組查詢
帶GROUP的查詢就是通常所說的分組查詢,它將查詢結果按照某一欄位或某一些欄位的欄位值進行分組。我們就可以對每一組進行相應的操作,而一般的查詢(如上面介紹的查詢)都只能針對每一條記錄進行操作。
用於統計每一組的記錄個數。以下是分組查詢的語法格式:
SELECT column_expression[, count(*)] FROM table_name GROUP BY column_expression [HAVING condition_expression]
HAVING是可選的,它用於對形成的分組進行篩選,留下滿足條件condition_expression的組。
【例5.11】 要求查詢表student中各系學生的數量。
(1)對於此查詢,要按系(s_dept)來實現分組,相應的語句如下:
SELECT s_dept 系別, count(*) 人數 FROM student GROUP BY s_dept;
查詢結果如下:
系別 人數
--------------------------------
大資料技術系 2
計算機系 4
智慧技術系 2
(2)如果要查詢人數大於或等於2的系的學生數量分佈情況(每個繫有多少人),則可以用HAVING短語來實現。
SELECT s_dept 系別, count(*) 人數 FROM student GROUP BY s_dept HAVING count(*) >= 2;
(3)如果進一步要求在平均成績及格(s_avgrade >= 60)的學生中完成這種分組查詢,即對於平均成績及格的學生,如果要查詢他們人數大於或等於2的系的學生數量分佈情況,則可以先用WHERE子句來選擇及格的學生,然後用HAVING短語來實現分組查詢:
SELECT s_dept 系別, count(*) 人數 FROM student WHERE s_avgrade >= 60 GROUP BY s_dept HAVING count(*) >= 2
執行結果如下:
系別 人數
-------------------------
計算機系 3
智慧技術系 2
注意:WHERE子句應該在GROUP和HAVING之前出現。
注意:WHERE子句和HAVING短語的作用都一樣,都是用於指定查詢條件。它們是有區別的:
HAVING短語是用於對組設定條件,而不是具體的某一條記錄,從而使得SELECT語句可以實現對組進行篩選;
WHERE子句是對每一條記錄設定條件的,而不是一個記錄組。
5.4.7 帶LIKE的匹配查詢和帶IS的空值查詢
1. 帶LIKE的匹配查詢
模糊查詢在大多情況下都是由謂詞LIKE來實現。其一般語法格式為:
SELECT column_expression FROM table_name WHERE column_name [NOT] LIKE character_string;
column_name的型別必須是字串型別,character_string表示字串常數。
該語句的含義是查詢欄位column_name的欄位值與給定字串character_string相匹配的記錄。
字串character_string可以是一個字串常量,也可以是包含萬用字元“_”和“%”的字串。是否相匹配要根據下列原則來確定:
“_”(下劃線): 它可以與任意的單字元相匹配。
“%”(百分號) :它可以與任意長度字串(包括空值)相匹配。
除了字元“_”和“%”外,所有其它的字元都只能匹配自己。
【例5.12】 查詢所有姓“王”的學生,並列出他們的學號、姓名、性別、平均成績和系別。
SELECT s_no 學號,s_name 姓名,s_sex 性別,s_avgrade 平均成績,s_dept 系別 FROM student WHERE s_name LIKE '王%';
該語句的查詢結果如下:
學號 姓名 性別 平均成績 系別
---------------------------------------------------------------------------------
20170202 王曉珂 女 88.1 計算機系
20170203 王偉志 男 89.8 智慧技術系
這是因為字串'王%'可以與任何第一個字為“王”的名字相互匹配。如果謂詞LIKE後跟'王_',則只能找出任何王姓且姓名僅由兩個字組成的學生;如果謂詞LIKE後跟'%志%',則表示要找出姓名中含有“志”的學生。
注意,由於欄位s_name的資料型別是固定長度的8個字元(char(8)),因此如果s_name值的實際長度不夠8個字元的,則後面以空格填補。
2. 空值null的查詢 IS
空值null的查詢是指查詢指定欄位的欄位值為null的記錄。對於這種查詢,首先想到的方法可能就是用帶等號“=”的WHERE子句來實現。但這種查詢方法是失敗的。
例如,下列的SELECT語句將找不到任何的記錄,即使存在s_avgrade的欄位值為null的記錄:
SELECT * FROM student WHERE s_avgrade = null -- 錯誤
而正確的寫法應該是:
SELECT * FROM student WHERE s_avgrade IS null -- 正確
【例5.13】 查詢所有欄位s_avgrade的值為非空的記錄。
SELECT * FROM student WHERE s_avgrade IS NOT null
5.4.8 使用ORDER排序查詢結果
有時候我們希望將查詢結果按照一定的順序進行排列,以方便、快速地從結果集中獲取我們需要的資訊。
例如,按照學生的成績從高到低進行排序,這樣我們一眼就可以看出誰的分數最高、誰的最低。而帶ORDER BY子句的SELECT語句就可以實現對查詢結果進行排序。其一般語法格式如下:
SELECT column_expression FROM table_name ORDER BY column_name [ASC|DESC][,…]
column_name表示排序的依據欄位,ASC表示按依據欄位進行升序排列,DESC表示按依據欄位進行降序排列。如果ASC和DESC都沒有選擇,則按依據欄位進行升序排列,即ASC為預設值。
【例5.14】 對錶student中的男同學按成績進行降序排序。
SELECT * FROM student WHERE s_sex = '男' ORDER BY s_avgrade DESC
執行後結果如下:
學號 姓名 性別 出生日期 專業 平均成績 系別
---------------------------------------------------------------------------------------------------------------------------------------
20170203 王偉志 男 1996-12-12 智慧科學與技術 89.8 智慧技術系
20170207 蒙恬 男 1995-12-02 大資料技術 78.8 大資料技術系
20170204 嶽志強 男 1998-06-01 智慧科學與技術 75.8 智慧技術系
20170205 賈簿 男 1994-09-03 計算機軟體與理論 43.0 計算機系
【例子】 如果希望在成績相同的情況下,進一步按照學號進行升序排列,則可以通過在ORDER BY後面增加欄位s_no的方法來實現。相應的語句如下:
SELECT * FROM student WHERE s_sex = '男' ORDER BY s_avgrade DESC, s_no ASC
在上面的語句中,排序的原理是這樣的:首先按照平均成績對記錄進行降序排序(因為選擇了DESC);如果查詢結果包含有平均成績相同的記錄,那麼這時按平均成績就無法對這些具有相同平均成績的記錄進行排序,這時SELECT語句將自動按照下一欄位——學號(s_no)對這些記錄進行升序排序(升序是預設排序方式);如果s_no後面還有其他欄位,那麼排序原理也依次類推。
5.4.9 連線查詢
同時涉及到兩個或者兩個以上資料表的查詢稱為連線查詢。連線查詢可以找出多個表之間蘊涵的有用資訊,實際上它是關係資料庫中最主要的查詢。連線查詢主要包括等值連線查詢、自然連線查詢、外連線查詢以及交叉連線查詢等。但交叉連線查詢沒有實際意義,且運用的很少,在此不作介紹。
連線查詢涉及到兩種表。除了表5.10所示的資料表student以外,我們還需建立另一張資料表——選課表SC。該表的結構和內容分別如表5.11和表5.12所示。
先用下列語句建立選課表SC:
CREATE TABLE SC( s_no char(8), c_name varchar(20), c_gradenumeric(3,1) CHECK(c_grade >= 0 AND c_grade <= 100), PRIMARY KEY(s_no, c_name) --將(s_no, c_name)設為主鍵 );
然後用下列INSERT插入如表5.12所示的資料,以便觀察連線查詢的效果:
INSERT SC VALUES('20170201','英語',80.2); INSERT SC VALUES('20170201','資料庫原理',70.0); INSERT SC VALUES('20170201','演算法設計與分析',92.4); INSERT SC VALUES('20170202','英語',81.9); INSERT SC VALUES('20170202','演算法設計與分析',85.2); INSERT SC VALUES('20170203','多媒體技術',68.1);
不特別說明,在本節中介紹的連線查詢主要是基於表student和表SC進行的。
1. 等值連線和自然連線查詢
在使用連線查詢時,按照一定的條件在兩個或多個表中提取資料並組成新的記錄,所有這些記錄的集合便構成了一個新的關係表。那麼這個條件就稱為連線條件,表示為join_condition。連線條件具有下列的形式:
[table1_name.]columni_name comp_oper [table2_name.]columnj_name
其中,comp_oper表示比較操作符,主要包括=、>(大於)、<(小於)、>=(大於等於)、<=(小於等於)、!=(不等)。連線條件有意義的前提是欄位column1_name和column2_name是可比較的。
當連線條件的比較操作符comp_oper為等號“=”時,相應的連線就稱為等值連線。對於表table1_name和表table2_name之間的等值連線,其一般格式可以表示如下:
SELECT [table1_name.]column1_name[, …], [table2_name.]column1_name[, …] FROM table1_name, table2_name WHERE [table1_name.]columni_name = [table2_name.]columnj_name
注意,對於欄位名前面的表名是否需要顯式標出,這取決於兩個表中是否都有與此同名的欄位。如果有,則必須冠以表名,否則可以不寫表名。
連線條件中相互比較的兩個欄位必須是可比的,否則比較將無意義。但可比性並不意味著兩個欄位的資料型別必須一樣,而只要求它們在語義上可比即可。
【例子】 對於整型的欄位和浮點型的欄位,雖然資料型別不同,但它們卻是可比的;而將整型的欄位和字串型的欄位進行比較,那是無意義的。
對於連線操作,可以這樣來理解:首先取表table1_name中的第一條記錄,然後從表table2_name中的第一條記錄開始依次掃描所有的記錄,並檢查表table1_name中的第一條記錄與表table2_name中的當前記錄是否滿足查詢條件,如果滿足則將這兩條記錄並接起來,形成結果集中的一條記錄。當對錶table2_name掃描完了以後,又從表table1_name中的第二條記錄開始,重複上面相同的操作,直到表table1_name中所有的記錄都處理完畢為止。
【例5.15】 要求查詢選課學生的學號、姓名、性別、專業、系別以及所選的課程名稱和成績。
這個查詢要求就涉及到兩個表的查詢,因為學生的基本資訊包含在表student中,而選課資訊則包含在表SC中。一個學生是否選課可以這樣獲知:如果表SC中有他的學號,則表明該學生已經選課,否則沒有選課。上述查詢問題就可以表述為,掃描表student和表SC中的每一條記錄,如果這兩個表中的當前記錄在各自的欄位s_no上取值相等,則將這兩條記錄按照指定的要求並接成一個新的記錄並新增到結果集中。這種查詢是以表student中的欄位s_no和表SC中的欄位s_no是否相等為查詢條件的,所以這種查詢就是等值查詢。該等值查詢的實現語句如下:
SELECT student.s_no 學號, s_name 姓名, s_sex 性別, s_speciality 專業, s_dept 系別, c_name 課程名稱, c_grade 課程成績 FROM student, SC WHERE student.s_no = SC.s_no
執行後的結果如下:
學號 姓名 性別 專業 系別 課程名稱 課程成績
---------------------------------------------------------------------------------------------------------------------------------------
20170201 劉洋 女 計算機應用技術計算機系 資料庫原理 70.0
20170201 劉洋 女 計算機應用技術計算機系 演算法設計與分析 92.4
20170201 劉洋 女 計算機應用技術計算機系 英語 80.2
20170202 王曉珂 女 計算機軟體與理論計算機系 演算法設計與分析 85.2
20170202 王曉珂 女 計算機軟體與理論計算機系 英語 81.9
20170203 王偉志 男 智慧科學與技術智慧技術系 多媒體技術 68.1
表student和表SC中都有欄位s_no,必須在其前面冠以表名,以明確s_no是屬於哪一個表中的欄位。如果在涉及的兩個表中還有其他同名的欄位,也需進行同樣的處理。
如果覺得表名過長,使用起來比較麻煩,則可以利用AS來定義別名,通過使用別名來進行表的連線查詢。
【例子】 上述的等值連線查詢語句跟下面的查詢語句是等價的:
SELECT a.s_no 學號, s_name 姓名, s_sex 性別, s_speciality 專業, s_dept 系別, c_name 課程名稱, c_grade 課程成績 FROM student as a, SC as b WHERE a.s_no = b.s_no
上述SELECT語句中,利用AS將表名student和SC分別定義為a和b,然後通過a和b來進行連線查詢,從而簡化程式碼。
如果在上述的等值連線查詢語句中去掉WHERE子句,則得到下列的SELECT語句:
SELECT student.s_no 學號, s_name 姓名, s_sex 性別, s_speciality 專業, s_dept 系別, c_name 課程名稱, c_grade 課程成績 FROM student, SC
該語句將形成表student和表SC的笛卡兒積。笛卡兒積是將兩個表中的每一條記錄分別並接而得到的結果集。笛卡兒積中記錄的條數是兩個表中記錄條數的乘積,
自然連線實際上是一種特殊的等值連線,這種連線在等值連線的基礎上增加以下兩個條件而形成的:
(1)參加比較的兩個欄位必須是相同的,即同名同類型;
(2)結果集的欄位是參加連線的兩個表的欄位的並集,但去掉了重複的欄位。
【例5.16】 實現表student和表SC的自然連線查詢。
可用下列的SELECT語句來實現:
SELECT student.s_no 學號, s_name 姓名, s_sex 性別, s_birthday 出生日期, s_speciality 專業, s_avgrade 平均成績,s_dept 系別, c_name 課程名稱, c_grade 課程成績 FROM student, SC WHERE student.s_no = SC.s_no
上述語句執行得到的自然連線結果如表5.13所示。
該結果集中的欄位包含了表student和表SC中所有的欄位,並去掉了重複欄位SC.s_no而保留student.s_no(當然,也可以去掉student.s_no而保留SC.s_no),而且無其他重複欄位,所以該等值查詢是自然連線查詢。
2. 自連線查詢
以上介紹的都是基於兩個不同的表進行連線查詢。但有時候需要將一個表跟它自身進行連線查詢,以完成相應的查詢任務,這種查詢就稱為自連線查詢。
在使用自連線查詢時,雖然實際操作的是同一張表,但在邏輯上要使之分為兩張表。這種邏輯上的分開可以在SQL Server中通過定義表別名的方法來實現,即為一張表定義不同的別名,這樣就形成了有相同內容、但表名不同的兩張表。
【例5.17】 要求查詢表student中與“劉洋”在同一個專業的所有學生的學號、姓名、性別、專業和平均成績(包括“李好”)。
這種查詢的難處在於,我們不知道“劉洋”的專業是什麼,如果知道了她的專業,那麼該查詢就很容易實現。必須從姓名為“劉洋”的學生記錄中獲得她的專業,然後由專業獲取相關學生的資訊。這種查詢難以用單表查詢來實現。如果使用自連線查詢,那麼問題就很容易得到解決。
自連線查詢的方法如下:為表student建立一個別名b,這樣student和b便形成邏輯上的兩張表。然後通過表student和表b的連線查詢實現本例的查詢任務。但這種自連線查詢要用到JOIN…ON…子句。查詢語句如下:
SELECT b.s_no 學號, b.s_name 姓名, b.s_sex 性別,b.s_speciality 專業, b.s_avgrade 平均成績 FROM student AS a-- 為student建立別名a JOIN student AS b -- 為student建立別名b ON (a.s_name='劉洋' AND a.s_speciality = b.s_speciality);
該語句執行結果如下:
學號 姓名 性別 專業 平均成績
--------------------------------------------------------------------------
20170201劉洋 女 計算機應用技術 98.5
20170206李思思 女 計算機應用技術 67.3
定義student的一個別名也可以實現此功能:
SELECT b.s_no 學號, b.s_name 姓名, b.s_sex 性別,b.s_speciality 專業, b.s_avgrade 平均成績 FROM student JOIN student AS b -- 為student建立別名b ON (student.s_name='劉洋' AND student.s_speciality = b.s_speciality);
3. 外連線查詢
上述介紹的連線查詢中,只有那些滿足查詢條件的記錄才被列出來,而不滿足條件的記錄則“不知去向”,這在有的應用中並不合適。
例如,在對錶student和表SC進行等值連線查詢後,學號為“20120204”等學生由於沒有選課,所以在查詢結果中就沒有關於這些學生的資訊。但是很多時候我們希望能夠將所有學生資訊全部列出,對於沒有選課的學生,其對應課程欄位和課程成績欄位留空即可。上述連線查詢方法就不適用了,需要引進另一種連線查詢——外連線查詢。
外連線查詢的語法格式為:
SELECT [table1_name.]column1_name[, …], [table2_name.]column1_name[, …] FROM table1_name LEFT|RIGHT [OUTER] JOIN table2_name ON join_condition
如果在FROM子句中選擇關鍵字LEFT,則該查詢稱為左外連線查詢;如果選擇關鍵字RIGHT,則該查詢稱為右外連線查詢。
在左外連線查詢中,對於表table1_name(左邊的表)中的記錄不管是否滿足連線條件join_condition,它們都將將被列出;而對錶table2_name(右邊的表)中的記錄,只有滿足連線條件join_condition的部分才被列出。
在右外連線查詢中,對於表table2_name中的記錄不管是否滿足連線條件join_condition,它們都將將被列出;而對錶table1_name中的記錄,只有滿足連線條件join_condition的部分才被列出。
【例5.18】 查詢所有學生的基本資訊,如果他們選課了則同時列出相應的課程資訊(含姓名、性別、專業、系別以及課程名稱和課程成績)。
這種查詢的基本要求是,首先無條件地列出所有學生相關資訊;其次對於已經選課的學生,則列出其相應的選課資訊,而對於沒有選課的學生,其相應的欄位留空。即表student中的記錄要無條件列出,而表SC中的記錄只有滿足連線條件(已選課)的部分才能列出。這需要用外連線查詢來實現,其實現語句如下:
SELECT s_name 姓名, s_sex 性別, s_speciality 專業, s_dept 系別, SC.c_name 課程名稱, SC.c_grade 課程成績 FROM student
LEFT JOIN SC ON (student.s_no = SC.s_no);
以上採用的是左外連線查詢。也可以將表student和表SC的位置調換一下,改用右外連線查詢,其實現語句如下:
SELECT s_name 姓名, s_sex 性別, s_speciality 專業, s_dept 系別, SC.c_name 課程名稱, SC.c_grade 課程成績 FROM SC RIGHT JOIN student ON (student.s_no = SC.s_no);
以上兩種外連線查詢語句的作用都是一樣的,執行後都得到如下結果:
姓名 性別 專業 系別 課程名稱 課程成績
----------------------------------------------------------------------------------------------------------------------------
劉洋 女計算機應用技術計算機系 資料庫原理70.0
劉洋 女計算機應用技術計算機系 演算法設計與分析92.4
劉洋 女計算機應用技術計算機系 英語80.2
王曉珂 女計算機軟體與理論計算機系 演算法設計與分析85.2
王曉珂 女計算機軟體與理論計算機系 英語81.9
王偉志 男智慧科學與技術智慧技術系 多媒體技術68.1
嶽志強 男智慧科學與技術智慧技術系 NULLNULL
賈簿 男計算機軟體與理論計算機系 NULLNULL
李思思 女計算機應用技術計算機系 NULLNULL
蒙恬 男大資料技術大資料技術系 NULLNULL
張宇 女大資料技術大資料技術系 NULLNULL
從以上結果可以看出,“嶽志強”等雖然沒有選課,但他們的基本資訊還是被列出了,其相應顯示選課資訊的位置則留空(NULL)。
5.4.10 巢狀查詢
一個查詢A可以嵌入到另一個查詢B的WHERE子句中或者HAVING短語中,由這種嵌入方法而得到的查詢就稱為嵌入查詢。查詢A稱為查詢B的子查詢(或內層查詢),查詢B稱為查詢A的父查詢(或外層查詢、主查詢等)。
觀察下面的例子:
SELECT s_no,s_name -- FROM student ----父查詢 WHERE s_no IN( -- SELECT s_no -- FROM SC ----子查詢 WHERE c_name = '演算法設計與分析') --
該查詢就是一個巢狀查詢,它找出了選“演算法設計與分析”這門課的學生的學號和姓名。其中,括號內的查詢為子查詢,括號外的查詢為父查詢。子查詢還可以巢狀其他的子查詢,即SQL語言允許多層巢狀查詢。
執行巢狀查詢的過程:首先執行最內層的子查詢,然後用子查詢的結果構成父查詢的WHERE子句,並執行該查詢;父查詢產生的結果又返回給其父查詢的WHERE子句,其父查詢又執行相同的操作,直到最外層查詢執行完為止。也就是說,巢狀查詢的執行過程是由裡向外。
巢狀查詢的優點是,每一層的查詢都是一條簡單的SELECT語句,其結構清晰,易於理解。但不能對子查詢的結果進行排序,即子查詢不能帶ORDER BY子句。
以下將根據所使用謂詞的不同來介紹各種巢狀查詢,並且假設討論的巢狀查詢是由子查詢和父查詢構成。對於多層的巢狀查詢,不難由此推廣。
1. 使用謂詞IN的巢狀查詢
帶IN的巢狀查詢是指父查詢和子查詢是通過謂詞IN來連線的一種巢狀查詢,也是用得最多的巢狀查詢之一。其特點是,子查詢的返回結果被當作是一個集合,父查詢則判斷某一欄位值是否在該集合中,以確定是否要輸出該欄位值對應的記錄的有關資訊。
【例5.19】 查詢“王偉志”和“蒙恬”所在專業的所有學生資訊。
這個查詢的解決過程是,首先找出他們所在的專業,然後根據專業來查詢學生。為此可以分為兩步走。
首先確定“王偉志”和“蒙恬”所在的專業:
SELECT student.s_speciality FROM student WHERE student.s_name = '王偉志' OR student.s_name = '蒙恬‘
上述語句的返回結果是('智慧科學與技術', '大資料技術')。下一步要做的就是查詢所有專業為智慧科學與技術或大資料技術的學生。相應的SELECT語句如下:
SELECT * FROM student WHERE student.s_speciality IN ('智慧科學與技術', '大資料技術');
將中間結果(‘智慧科學與技術’, ‘大資料技術’)去掉,代之以產生該結果的SELECT語句,於是得到了下列的巢狀查詢:
SELECT * FROM student WHERE student.s_speciality IN ( SELECT student.s_speciality FROM student WHERE student.s_name = '王偉志' OR student.s_name = '蒙恬');
執行該巢狀查詢後得到如下的結果,該結果與我們預想的完全一致:
s_nos_names_sexs_birthday s_speciality s_avgrade s_dept
------------------------------------------------------------------------------------------------------------------------------
20170203王偉志 男1996-12-12 智慧科學與技術89.8智慧技術系
20170204嶽志強 男1998-06-01 智慧科學與技術75.8智慧技術系
20170207蒙恬 男1995-12-02 大資料技術 78.8大資料技術系
20170208張宇 女1997-03-08 大資料技術 59.3大資料技術系
對於這個例子,如果運用連線查詢,會顯得比較複雜。但使用帶IN的巢狀查詢,不管在問題的解決思路上還是在SELECT語句的構造上都顯得更具條理性和直觀性,而且僅涉及到一個邏輯表(不用建立別名)。
2. 使用比較運算子的巢狀查詢
比較運算子是指>、<、=、>=、<=、<>等符號。這些符號可以將一個欄位與一個子查詢連線起來構成一個邏輯表示式。以這個邏輯表示式為查詢條件的查詢就構成了父查詢。
一般來說,這種比較只能是基於單值進行的,所以要求子查詢返回的結果必須為單欄位值。
可以返回('智慧科學與技術'),但返回('智慧科學與技術', '大資料技術')則是不允許的。在例5.19中,如果將其巢狀查詢中的謂詞IN改為任意一個比較運算子,都會產生錯誤。原因是,該巢狀查詢中的子查詢返回的不是單欄位值,而是兩個欄位值。
【例5.20】 查詢所有平均成績比“蒙恬”低的學生,並列出這些學生的學號、姓名、專業和平均成績。
這個查詢的關鍵是首先要找出“蒙恬”的平均成績,然後才能據此找出其他有關學生的資訊。由於“蒙恬”的平均成績是唯一的,所以我們可以構造如下的子查詢:
SELECT s_avgrade FROM student WHERE s_name = '蒙恬';
該查詢返回的結果是78.8。然後由此構造父查詢:
SELECT s_no 學號, s_name 姓名, s_speciality 專業, s_avgrade 平均成績 FROM student WHERE s_avgrade < 78.8
把中間結果78.8去掉以後,將以上兩個查詢合起來,得到下列的巢狀查詢:
SELECT s_no 學號, s_name 姓名, s_speciality 專業, s_avgrade 平均成績 FROM student WHERE s_avgrade < ( SELECT s_avgrade FROM student WHERE s_name = '蒙恬')
上述巢狀查詢後結果如下:
學號 姓名專業 平均成績
----------------------------------------------------------------------------------------
20170204嶽志強 智慧科學與技術75.8
20170205賈簿 計算機軟體與理論 43.0
20170206李思思 計算機應用技術67.3
20170208張宇 大資料技術 59.3
在SQL Server 2014中,子查詢在比較運算子之後或者之前都無關緊要,只要查詢條件返回的真值一樣,則查詢結果都相同。例如,下列的巢狀查詢與上面的巢狀查詢是等價的,查詢結果都一樣:
SELECT s_no 學號, s_name 姓名, s_speciality 專業, s_avgrade 平均成績 FROM student WHERE ( SELECT s_avgrade FROM student WHERE s_name = '蒙恬') > s_avgrade;
3.使用謂詞EXISTS的巢狀查詢
在使用謂詞EXISTS的巢狀查詢中,只要子查詢返回非空的結果,則父查詢的WHERE子句將返回邏輯真,否則返回邏輯假。至於返回的結果是什麼型別的資料,對這種巢狀查詢是無關緊要,所以在父查詢的目標欄位表示式都用符號*,即使給出欄位名也無實際意義。
帶EXISTS的巢狀查詢與前面介紹的巢狀查詢的最大區別在於,它們執行的方式不一樣。帶EXISTS的巢狀查詢是先執行外層,後執行內層,再回到外層。具體講,對於每一條記錄,父查詢先從表中“抽取”出來,然後“放到”子查詢中並執行一次子查詢;如果該子查詢返回非空值(導致WHERE子句返回邏輯真),則父查詢將該記錄新增到結果集中,直到所有的記錄都被進行這樣的處理為止。顯然,父查詢作用的表中有多少條記錄,則子查詢被執行多少次。在這種查詢中,子查詢依賴於父查詢,所以這類查詢又稱為相關子查詢。
前面介紹的巢狀查詢中,先執行子查詢,後執行父查詢。子查詢與父查詢無關,所以這類查詢稱為不相關子查詢。
【例5.21】 查詢所有選修了《演算法設計與分析》的學生學號、姓名和專業。
學生選修課程的資訊放在表SC中,而學生的學號、姓名和專業資訊則放在表student中,所以該查詢要涉及到兩個表。顯然,該查詢可以用很多種方法來實現,下面我們考慮運用帶EXISTS的巢狀查詢來完成。相應的SELECT語句如下:
SELECT s_no 學號, s_name 姓名, s_speciality 專業 FROM student WHERE EXISTS( SELECT * FROM SC WHERE student.s_no = s_no AND c_name = '演算法設計與分析');
在執行該巢狀查詢時,父查詢先取表student中的第1條記錄,記為r1;然後執行一次子查詢,這時發現表SC中存在s_no欄位值與r1的s_no欄位值相等的記錄(記為r2),而且r2在c_name欄位上的取值為“演算法設計與分析”,所以子查詢返回記錄r2(非空);由於第1條記錄(r1)使得子查詢返回值為非空,所以父查詢的WHERE子句返回邏輯真,這樣父查詢便將第1條記錄新增到結果集中;重複上述過程,直到表student中所有的記錄都被處理完為止。
本查詢也可以用帶謂詞IN的巢狀查詢來實現,其查詢實現思想也比較直觀。首先用子查詢返回表SC中所有選修了《演算法設計與分析》的學生學號的集合,然後用父查詢找出表student中學號在該集合中的學生。相應的查詢語句如下:
SELECT s_no 學號, s_name 姓名, s_speciality 專業 FROM student WHERE s_no IN ( SELECT s_no FROM SC WHERE c_name = '演算法設計與分析');
5.4.11 查詢的集合運算
SELECT語句返回的結果是若干個記錄的集合。集合有其固有的一些運算,如並、交、差等。從集合運算的角度看,可以將每一個SELECT語句當作是一個集合。於是,可以對任意兩個SELECT語句進行集合運算。在SQL語言,提供了並(UNION)、交(INTERSECT)和差(EXCEPT)等幾個集合運算。
兩個查詢的並(UNION)是指將兩個查詢的返回結果集合併到一起,同時去掉重複的記錄。並運算的前提是,兩個查詢返回的結果集在結構上要一致,即結果集的欄位個數要相等以及欄位的型別要分別相同。
【例5.22】 查詢專業為大資料技術或者平均成績在良好以上(>=80)的學生,並列出他們的學號、姓名、專業和平均成績。
這個查詢可以看作是以下兩個查詢並:
-- 查詢專業為大資料技術的學生
SELECT s_no 學號, s_name 姓名, s_speciality 專業, s_avgrade 平均成績 FROM student WHERE s_speciality = '大資料技術';
-- 查詢平均成績在良好以上的學生
SELECT s_no 學號, s_name 姓名, s_speciality 專業, s_avgrade 平均成績 FROM student WHERE s_avgrade >= 80;
以上這兩個查詢語句執行後所得的結果分別如下:
學號姓名 專業 平均成績
-------------------------------------------------------------------------------
20170207 蒙恬 大資料技術 78.8
20170208 張宇 大資料技術 59.3
學號姓名專業 平均成績
-------------------------------------------------------------------------------
20170201 劉洋 計算機應用技術 98.5
20170202 王曉珂 計算機軟體與理論 88.1
20170203 王偉志 智慧科學與技術 89.8
將以上兩個SELECT語句用關鍵字UNION連起來就實現了兩個查詢的並:
(SELECT s_no 學號, s_name 姓名, s_speciality 專業, s_avgrade 平均成績 FROM student WHERE s_speciality = '大資料技術') UNION (SELECT s_no 學號, s_name 姓名, s_speciality 專業, s_avgrade 平均成績 FROM student WHERE s_avgrade >= 80);
以上語句執行後得到如下結果:
學號 姓名 專業 平均成績
--------------------------------------------------------------------
20170201 劉洋 計算機應用技術 98.5
20170202 王曉珂 計算機軟體與理論 88.1
20170203 王偉志 智慧科學與技術 89.8
20170207 蒙恬 大資料技術 78.8
20170208 張宇 大資料技術 59.3
這個結果正好是上述兩個查詢結果集的並。
【例5.23】 查詢專業為智慧科學與技術而且平均成績在良好以上(>=80)的學生,並列出他們的學號、姓名、專業和平均成績。
該查詢可以看作是專業為智慧科學與技術的學生集合和平均成績在良好以上的學生集合的交集。
基於交運算的SQL語句如下:
(SELECT s_no 學號, s_name 姓名, s_speciality 專業, s_avgrade 平均成績 FROM student WHERE s_speciality = '智慧科學與技術') INTERSECT (SELECT s_no 學號, s_name 姓名, s_speciality 專業, s_avgrade 平均成績 FROM student WHERE s_avgrade >= 80);
此SQL語句執行結果如下:
學號 姓名 專業 平均成績
------------------------------------------------------------------------------
20170203 王偉志 智慧科學與技術 89.8
【例5.24】 查詢專業為智慧科學與技術而且平均成績在良好以下(<80)的學生,並列出他們的學號、姓名、專業和平均成績。
該查詢可以看作是專業為智慧科學與技術的學生集合與平均成績在良好以上的學生集合的差集。
基於差運算的SQL語句如下:
(SELECT s_no 學號, s_name 姓名, s_speciality 專業, s_avgrade 平均成績 FROM student WHERE s_speciality = '智慧科學與技術') EXCEPT (SELECT s_no 學號, s_name 姓名, s_speciality 專業, s_avgrade 平均成績 FROM student WHERE s_avgrade >= 80);
此SQL語句執行結果如下:
學號姓名 專業 平均成績
----------------------------------------