全部章節 >>>>
本章目錄
3.1 子查詢定義和單行子查詢
3.1.1 子查詢定義
子查詢(subquery)是數據庫經常用到的一個操作,它不僅用在數據查詢語句中,在 DML 語句中也都會用到子查詢
子查詢將一個查詢語句嵌套(nest)在另一個查詢語句中,在特定情況下,一個查詢語句的條件需要另一個查詢語句來獲取,內層查詢語句的查詢結果可以爲外層查詢語句提供查詢條件
子查詢的實質:一個 select 語句的查詢結果能夠作爲另一個語句的輸入值。子查詢不僅可用於 where 子句中,還能夠用於 from 子句中,此時子查詢的結果將作爲一個臨時表(temporary table)來使用
子查詢還能以字段的形式出現在 select 語句的選擇列中。根據子查詢所返回的結果行數,可以將其分爲單行子查詢和多行子查詢
3.1.2 單行子查詢應用
單行子查詢指子查詢的返回結果只有一行數據。當在主查詢的條件語句中引用子查詢的結果時,可使用單行比較符(如=、>、<、>=、<= 和 < >)進行比較
查詢“戰爭”類題材電影的具體信息,要求輸出片名和導演名
示例:
(1)電影(movie)表包含電影名、導演名和電影類型編號信息,但並不包含電影類型名稱信息;電影類型(movie_type)表既包含電影類型編號信息又包含電影類型名稱信息。上述這兩張表的共同信息是電影類型編號,所以查詢時需連接電影表和電影類型表,並以電影類型編號作爲兩表的連接。首先,從電影類型表查詢出類型名稱爲“戰爭”的電影類型編號,使用 SQL1 作爲標記
select id from movie_type where typeName=' 戰爭 '
(2)根據 SQL1 查詢出的地區編號,在電影表中檢索出電影名和導演名信息。使用 SQL2 作爲標記,並將 SQL1作爲查詢條件代入 SQL2
select movieName 電影名 ,director 導演名 from movie where typeId=
(select id from movie_type where typeName=' 戰爭 ')
由於在 movie_type 表中 typeName 是唯一約束列,因而子查詢 SQL1 的執行結果只能有 1 條(單行子查詢)或 0 條記錄。
此例還可以採用連接查詢實現
select movieName 電影名 ,director 導演名 from movie m, movie_type mt
where m.typeId=mt.movie_type and typeName=' 戰爭 '
示例:
查詢票價高於平均票價的電影信息,要求輸出電影名和導演名
(1)獲得平均票價,使用 SQL1 標記
select avg(ticketPrice) from movie
(2)查詢票價大於平均票價的電影信息,要求輸出電影名和導演名。使用 SQL2 標記,並將 SQL1 作爲查詢條件代入 SQL2
select movieName 電影名 ,director 導演名 from movie where ticketPrice>( select avg(ticketPrice) from movie)
示例:
查詢票價高於“戰爭”類題材的電影信息,要求輸出電影名和導演名
(1)查詢類型名爲“戰爭”的電影類型編號,使用 SQL1 標記
select id from movie_type where typeName=' 戰爭 '
(2)查詢“戰爭”類題材電影的平均票價,使用 SQL2 標記,執行時將 SQL1 作爲查詢條件代入 SQL2
select avg(ticketPrice) from movie where typeID=(select id from movie_type where typeName=' 戰爭 ')
(3)查詢票價大於“戰爭”類題材電影平均票價的電影信息,要求輸出電影名和導演名。查詢語句使用 SQL3 標記,使用時將 SQL2 作爲條件代入 SQL3
select movieName 電影名 ,director 導演名 from movie where ticketPrice>
(select avg(ticketPrice) from movie where typeID=(
select id from movie_type where typeName=' 戰爭 '))
子查詢應用經驗初步歸納如下:
- 子查詢一般用於 select 語句的 where 子句中,且可以嵌套
- 編寫複雜的子查詢的解決思路是逐層分解查詢,即從最內層的子查詢開始分解,將嵌套的 SQL 語句拆分爲一個個獨立的 SQL 語句
- 子查詢的執行過程遵循“由裏及外”的原則,即先執行最內層的子查詢語句,然後將執行結果與外層的語句進行合併,依次逐層向外擴展並最終形成完整的 SQL 語句
- 一般情況下,連接查詢可改爲子查詢實現;但子查詢卻不一定可改爲連接查詢實現
- 子查詢與連接查詢執行效率的比較:當子查詢執行結果的行數較大,而主查詢執行結果的行數較小時,子查詢執行效率較高;反之,則連接查詢執行效率較高
3.1.4 實踐練習
3.2 多行子查詢應用
3.2.1 in 比較符
使用多行比較符 in 時,主查詢會與子查詢中的每一個值進行比較,如果與其中的任意一個值相同,則返回。not in 與 in 的含義恰好相反
查詢“戰爭”和“喜劇”類題材電影的相關信息,要求輸出片名和導演名
示例:
(1)查詢類型名爲“戰爭”和“喜劇”的電影類型編號
select id from movie_type where typeName=' 戰爭 ' or typeName=' 喜劇 '
(2)查詢“戰爭”和“喜劇”類題材電影的相關信息
select movieName 電影名 ,director 導演名 from movie where typeID in
(select id from movie_type where typeName=' 戰爭 ' or typeName=' 喜劇 ')
由於多行子查詢返回的結果行數可以爲一個,因而單行子查詢也是多行子查詢的一種特殊情況,所以單行子查詢的“=”比較符可以替換爲多行子查詢的“in”比較符。但不能將多行子查詢的“in”比較符替換爲單行子查詢的“=”比較符。
查詢客戶“zhang01”顧客所預訂電影的具體信息,要求輸出電影名、導演名、票價和片長,並按照票價升序排列
示例:
(1)查詢客戶“zhang01”的客戶編號
select id from customer where username='zhang01'
(2)查詢客戶“zhang01”所預訂的所有電影的排片編號
select scheduleId from ticket_sell where customerID in
(select id from customer where username='zhang01')
(3)查詢客戶“zhang01”所預訂的所有電影的電影編號
select movieId from `schedule` where id in (select scheduleId from ticket_sell where customerID in (select id from customer where username='zhang01'))
(4)查詢客戶“zhang01”所預訂電影的具體信息
select movieName 電影名 ,director 導演名 ,ticketPrice 票價(元),filmLength 片長(分鐘)
from movie where id in(select movieId from `schedule` where id in
(select scheduleId from ticket_sell where customerID in
(select id from customer where username='zhang01'))) order by ticketPrice
此示例嵌套較深,如果採用連接查詢寫法會相對簡潔,特別是當商品記錄數較大的情況下,連接查詢的效率會更高。使用連接查詢實現的 SQL 語句如下:
select distinct movieName 電影名 ,director 導演名 ,ticketPrice 票價(元),filmLength 片長(分鐘) from movie m,`schedule` s,ticket_sell ts,customer c where m.id=s.movieId and s.id=ts.scheduleId and ts.customerId=c.id and c.username='zhang01' order by ticketPrice
上面例子的子查詢實現很好地體現了子查詢應用的解決之道──“由裏及外”的原則,即逐層生成包含子查詢在內的主查詢,逐層生成的主查詢又成爲上一層主查詢所包含的子查詢,如此依次遞進(recursion),最終生成最上層的包含所有子查詢的主查詢。
all 關鍵字位於多行比較運算符之後,通過 all 關鍵字將一個表達式或列的值與子查詢所返回的一列值中的每一行進行比較,只要有一次比較的結果爲 false(假),則 all 測試返回 false,主查詢不執行;否則返回 true,執行主查詢
all 運算符的含義如下:
- 當 <all 時,表示小於最小值
- 當 >all 時,表示大於最大值
語法:
表達式或字段 多行比較運算符 all( 子查詢 )
查詢比所有“喜劇”類題材電影的票價都高的電影信息,要求輸出片名和導演名
示例:
(1)查詢類型爲“喜劇”的電影類型編號
select id from movie_type where typeName=' 喜劇 '
(2)查詢所有“喜劇”類題材電影的票價
select ticketPrice from movie where typeID=(
select id from movie_type where typeName=' 喜劇 ')
(4)查詢比所有“戰爭”類題材電影的票價都高的電影信息
select movieName 電影名 ,director 導演名 from movie where ticketPrice > all (select ticketPrice from movie where typeID=(select id from movie_type where typeName=' 喜劇 '))
由於“>all(子查詢)”的含義是“大於子查詢返回結果的最大值”,所以還可以採用“>(子查詢所獲取的最大列值)”的方式求解上面這個例子,該方法的 SQL 語句如下:
select movieName 電影名 ,director 導演名 from movie where ticketPrice > (select max(ticketPrice) from movie where typeID=( select id from movie_type where typeName=' 喜劇 ' ))
3.2.3 any|some 關鍵字子查詢
any 與 some 的查詢功能相同
any 或 some 用於子查詢之前,通過 any|some 比較運算符,將一個表達式或列的值與子查詢所返回的一列值中的每一行進行比較,只要有一次比較的結果爲 true,則 any 或 some 測試返回 true,主查詢執行;否則結果爲false,主查詢不執行
any|some 運算符的含義如下:
- 當 <any|some 時,表示小於最大值
- 當 =any|some 時,表示與 in 運算符等價
- 當 >any|some 時,表示大於最小值
語法:
表達式或字段 多行比較運算符 any|some(子查詢)
查詢比任意一個“喜劇”類題材電影的票價高的電影信息,要求輸出電影名和導演名
示例:
select movieName 電影名 ,director 導演名 from movie where ticketPrice > any
(select ticketPrice from movie where typeID=(
select id from movie_type where typeName=' 喜劇 '))
由於“>any(子查詢)”的含義是“大於子查詢返回結果的最小值”,所以還可以採用“>(子查詢所獲取的最小列值)”的方式求解上面這個例子,該方法的 SQL 語句如下:
select movieName 電影名 ,director 導演名 from movie where ticketPrice > (select min(ticketPrice) from movie where typeID=( select id from movie_type where typeName=' 喜劇 ' ))
3.2.4 實踐練習
3.3 子查詢特殊應用
3.3.1 from 子句中的子查詢
子查詢通常用於 where 子句中,但其也可在 from 子句和 select 子句中使用
示例:
影院在線售票系統爲了提升影片的上座率,優化票價定價的科學性。爲了瞭解每一個影片的票價與該類題材電影整體票價的對照關係,比較合適的做法是在顯示每一個影片票價的同時,顯示所屬類型電影的平均票價
select mt.typeName 電影類型 , m.movieName 電影名 , m.director 導演名 , round(m.ticketPrice,2)
票價(元), round(A.avgPrice,2) 該電影類型平均票價(元) from movie m, movie_type mt,
(select typeId,avg(ticketPrice) avgPrice from movie group by typeId) A where m.typeId=mt.id and m. typeId=A.typeId order by mt.id
3.3.2 select 子句中的子查詢
在 select 子句中使用子查詢,其實質是將子查詢的執行結果作爲 select 子句的列,可以起到與連接查詢異曲同工的作用
示例:
分別獲取張藝謀所導演影片的上映數量以及上映班次
(1)獲取張藝謀所導演影片的上映數量
select count(*) 張藝謀所導演影片的上映數量 from movie where director=' 張藝謀 '
(2)獲取張藝謀所導演影片的上映班次
select count(movieId) 張藝謀所導演影片的上映班次 from `schedule` where movieId in
(select id from movie where director=' 張藝謀 ')
(3)將(1)和(2)獲得的查詢結果,即張藝謀所導演影片的上映數量和上映班次作爲 select 子句的查詢列,其形式即爲 select 子句中的子查詢。
select count(*) 張藝謀所導演影片的上映數量 , (select count(movieId) from `schedule` where
movieId in (select id from movie where director=' 張藝謀 ')) 張藝謀所導演影片的上映班次
from movie where director=' 張藝謀 '
exists 用於檢查子查詢是否至少會返回一行數據,該子查詢實際上並不返回任何數據,而是返回值 true 或false。exists 指定一個子查詢,用於檢測行的存在。當子查詢的行存在時,則執行主查詢表達式,否則不執行
查詢所有通過影院在線售票系統預訂電影票的客戶姓名
語法:
主查詢表達式 [not] exists ( 子查詢 )
示例:
(1)主查詢用於從客戶表獲取客戶姓名。
(2)exists 指定的子查詢將從售票表中獲取滿足“客戶編號 = 客戶表 . 客戶編號”條件的任意數據。
(3)只要 exists 子查詢的結果集中有數據行返回,exists 子查詢的返回結果若爲 true,則執行主查詢獲得所有預訂電影票的客戶姓名;exists 子查詢的返回結果若爲 false,則不執行主查詢。最終的 SQL 語句如下:
select customerName 客戶姓名 from customer c where exists
(select * from orders where customerID=c.customerID)
3.3.4 實踐練習
3.4 DML 語句中的子查詢
3.4.1 update 子句中的子查詢
- 子查詢不僅可在 select 語句中使用,以實現需要嵌套的查詢功能,還可以維護數據,完成複雜的更新、刪除和插入功能
- 爲了完成上述數據維護功能,需要在 DML 的 update 語句、delete 語句和 insert 語句中使用子查詢
- 在 DML 語句中使用子查詢與在 select 語句中使用子查詢的原理是一致的,均爲將內層子查詢的結果作爲外層主查詢中 where 條件的參考值來使用
示例:
爲響應政府提升公民災難意識和應對能力的號召,院線將所有災難片電影的票價降低 20%
(1)在子查詢中獲取類型爲“災難”的電影類型編號。
(2)在主查詢中,使用 update 語句將所有災難片電影的票價降低 20%
update product set currentPrice=currentPrice*0.9 where categoryID in(
select categoryID from category where categoryName=' 災難 ')
示例:
計算所有客戶預訂電影票的總金額,並使用該金額更新客戶表中“累計訂票金額”的字段值
(1)在售票表中,根據客戶編號進行分組,並使用“sum( 實際票價 )”彙總出每個客戶的總訂票金額
select customerID 客戶編號 , sum(purchasePrice) 總訂票金額(元) from ticket_sellgroup by customerID
(2)因爲要將(1)中彙總出的每個客戶的總訂票金額賦給客戶表中的“累計訂票金額”字段,所以可以將(1)中的 SQL 作爲子查詢,並在主查詢中執行“update 客戶表 set 累計購票金額 =( 子查詢中獲取的每個客戶的總訂票金額 )”。
(3)爲實現 update 語句,需在子查詢中刪除選擇列“客戶編號”,並且爲了建立主查詢與子查詢的關聯,還需要在子查詢的 where 條件中設定“客戶編號 = 售票表 . 客戶編號”
update customer c set totalFee=(select sum(purchasePrice) from ticket_sell where customerID=c.ID group by customerID)
使用子查詢刪除客戶“chen01”
示例:
刪除數據時需要考慮表的主從關係,正確的做法是先刪除從表數據,再刪除主表數據。
(1)使用子查詢刪除售票表中客戶“chen01”所有的訂票記錄
delete from ticket_sell where customerID in
(select id from customer where username='chen01')
(2)刪除客戶表中客戶“chen01”的記錄
delete from customer where username='chen01'
3.4.3 實踐練習
總結:
- 子查詢將一個查詢語句嵌套在另一個查詢語句中,在特定情況下,一個查詢語句的條件需要另一個查詢語句來獲取
- 比較運算符 all 關鍵字用於子查詢之前,通過該關鍵字將一個表達式或列的值,與子查詢所返回的一列值中的每一行進行比較
- exists 用於檢測行的存在,該子查詢實際上並不返回任何數據,而是返回值 true 或false。當子查詢的行存在時,則執行主查詢表達式,否則不執行
- 在 DML 語句中使用子查詢與在 select 語句中使用子查詢的原理是一致的,均爲將內層子查詢的結果作爲外層主查詢中 where 條件的參考值來使用