SQL——嵌套子查詢

子查詢是嵌套在另一個查詢中的select-from-where表達式。子查詢嵌套在where子句中透葛,通常用于對集合成員資格、集合的比較以及集合的基數(shù)進行檢查沪悲。

1获洲、集合成員資格

SQL允許測試元組在關(guān)系中的成員資格。連接詞in測試元組是否是集合中的成員殿如,集合是有select子句產(chǎn)生的一組值組成贡珊。連接詞not in則測試元組是否不是結(jié)合中的成員。

示例:

#找出在2018年春季和秋季同時開課的課程 
#前面有提到in 可以替代集合的交運算
#not in 就不舉例了涉馁,相信讀者也能夠?qū)懗觯╪ot in 相當于集合的差運算)
SELECT DISTINCT S.course_id
FROM section AS S
WHERE S.semester='spring'AND S.year=2018 AND
course_id IN (SELECT course_id
                FROM section as T
                WHERE  T.semester = 'fall' and T.year=2018
);

同時门岔,in和not in也能用于枚舉型集合。例如:

-- 找出名字不叫Smith 和wu的所有教師的信息
SELECT DISTINCT *
FROM instructor
WHERE name NOT IN ('smith','wu');
instructor表
結(jié)果表

2烤送、集合的比較

-- 找出比其中一個歷史系老師工資高的教師信息
SELECT DISTINCT T.*
FROM instructor AS T,instructor AS S
WHERE T.salary>S.salary AND S.dept_name = 'history';

對于上面的查詢,可以用另一種方式書寫寒随,短語“至少比某一個大”在SQL中用>some表示,于是可以用下面這種更加貼切的文字表達的形式書寫:

SELECT *
FROM instructor
WHERE salary>SOME ( SELECT salary 
                    FROM instructor   
                    WHERE dept_name = 'history');
兩種書寫形式的結(jié)果表
instructor原表

同樣的帮坚,也有<some,=some,<=some,>=some,和<>some的比較,其中=some等價于in妻往,<>some不等價于not in;

當我們要找出比所有歷史系老師工資高的教師信息時试和,可以通過>all來實現(xiàn)讯泣,例如:

SELECT *
FROM instructor
WHERE salary>ALL (SELECT salary FROM instructor WHERE dept_name = 'history');
>all結(jié)果表

同some,也有<all,<=all,>=all,=all,<>all的比較阅悍,<>all 等價于not in好渠,但=all并不等價于in昨稼。

3、空關(guān)系測試

SQL還有一個特性是可測試一個子查詢的結(jié)果中是否存在元組拳锚。exists結(jié)構(gòu)在作為參數(shù)的子查詢非空時返回true值假栓。使用exists,我們還能夠使用另一種方式書寫實現(xiàn)上文實現(xiàn)“找出在2018年春季和秋季同時開課的課程 ”的查詢霍掺。

SELECT DISTINCT course_id
FROM section AS  S
WHERE S.semester = 'spring' AND S.year = 2018 AND
  exists(SELECT *
         FROM section AS T
         WHERE T.semester = 'fall' AND T.year=2018 AND S.course_id=T.course_id);

上述的示例中還反應(yīng)了SQL的另一特性匾荆,來自外層查詢的一個相關(guān)名稱(上述查詢中的S)可以用在where子句的子查詢中。使用外層查詢相關(guān)名稱的子查詢稱作相關(guān)子查詢抗楔。

在包含了子查詢的操作中棋凳,在相關(guān)名稱上可以應(yīng)用作用域規(guī)則。根據(jù)此規(guī)則连躏,在一個子查詢中只能使用該子查詢本身定義的剩岳,或者包含該子查詢的任何查詢中定義的相關(guān)名稱。emmm……類似于全局變量于局部變量的作用域關(guān)系入热。

同理拍棕,可以用not exists結(jié)構(gòu)來測試子查詢結(jié)果集中是否不存在元組。

同時勺良,我們還可以使用not exists結(jié)構(gòu)來模擬集合的包含操作:將“關(guān)系A(chǔ)包含于關(guān)系B”寫成not exists(B except A)绰播,例如:

#找出選修了biology系開設(shè)的所有課程的學生
#takes關(guān)系表是表示某一個學生所選修的課程集合

SELECT S.id,S.name
FROM student AS S 
WHERE NOT exists(
    (
      #找出biology系開設(shè)的所有課程集合
      SELECT course_id
      FROM course
      WHERE dept_name = 'biology'
    )EXCEPT (
      #找出S.id選修的所有課程集合
      SELECT T.course_id
      FROM takes AS T 
      WHERE S.id = T.id
    )
);
#外層select對每個學生測試其選修的所有課程集合是否包含biology系開設(shè)的所有課程。

使用mysql的人(因為mysql沒有except運算尚困,可以參照之前SQL——集合運算)蠢箩,可以用下面句子實現(xiàn)上述效果:

select distinct S.id , S.name
from student as S ,takes as T
where S.id = T.id and course_id in (
#找出biology系開設(shè)的course_id集合
select course_id
from course
where dept_name = 'biology');

也可以使用連接的方式替換上面的方式:

SELECT S.id,S.name
FROM student AS S
WHERE S.id = SOME (
  SELECT id
  FROM
  (
#找出biology系的course_id關(guān)系,再將這個關(guān)系通過course_id與takes連接
    SELECT course_id
    FROM course
    WHERE dept_name = 'biology'
  )AS C
  JOIN takes AS T USING (course_id)
);
結(jié)果表
takes表
student表
course表

注:數(shù)據(jù)庫的數(shù)據(jù)純屬虛構(gòu)事甜,測試使用谬泌;

其實,之前有講到如何替換except逻谦,如果按照之前的替換方法掌实,應(yīng)該是以下的書寫形式的:
但是,這樣書寫是有錯誤的邦马。為什么呢贱鼻?因為在子查詢中join子句是無法參照(或者說引用更合適)外部表的,也就說這里會報這樣的錯誤:' Unknown column 'S.id' in 'where clause'滋将。

這也提醒我們邻悬,實現(xiàn)某個查詢功能,首先搞清楚它的實現(xiàn)邏輯随闽,這些邏輯邏輯可能有好幾種實現(xiàn)方法父丰。

SELECT S.id,S.name
FROM student AS S
WHERE NOT exists(
  SELECT course_id
  FROM
  (
    SELECT course_id
    FROM course
    WHERE dept_name = 'biology'
  )AS C
  LEFT JOIN
  (
    SELECT course_id
    FROM takes AS T
    WHERE S.id = T.id
  )AS N USING (course_id)
  WHERE N.course_id IS NULL
);

4、重復元組存在性測試

SQL提供一個布爾函數(shù)unique橱脸,用于測試在一個子查詢的結(jié)果中是否存在重復元組础米。如果作為參數(shù)的子查詢結(jié)果中沒有重復的元組,unique結(jié)構(gòu)將返回true值添诉。

-- 找出所有在2018年最多開設(shè)一次的課程
SELECT T.*
FROM course AS T
WHERE UNIQUE(
    SELECT R.course_id
    FROM section AS R
    WHERE T.course_id=R.course_id AND R.year = 2018
);

不過令人蛋疼的是屁桑,mysql沒法識別出來,不過沒關(guān)系栏赴,我們可以用下列方法來等價它:

#不重復蘑斧;如果要測試重復,則將等于替換成<
SELECT T.*
FROM course AS T
WHERE 1 = (
  SELECT count(R.course_id)
  FROM section AS  R
  WHERE T.course_id=R.course_id AND R.year = 2018
);

5须眷、from子句中的子查詢

SQL允許在from子句中使用子查詢表達式竖瘾。再次采用的主要觀點是:任何select-from-where表達式返回的結(jié)果都是關(guān)系,因而可以插入到另一個select-from-where中任何關(guān)系可以出現(xiàn)的位置花颗。對于下面這個查詢句子捕传,是找出系平均工資超過15000的那些戲中的教師的平均工資

SELECT dept_name,avg(salary) as avg_salary
FROM instructor
GROUP BY dept_name
HAVING avg(salary)>15000;

如果在from插入子查詢實現(xiàn),則如下:

SELECT dept_name,avg_salary
FROM (
  SELECT dept_name,avg(salary) AS avg_salary
  FROM instructor
  GROUP BY dept_name
)AS S 
WHERE S.avg_salary>15000;

注:很多SQL實現(xiàn)都支持在from子句中嵌套子查詢扩劝,還有就是SQL實現(xiàn)要求對一個子查詢結(jié)果關(guān)系都給一個名字庸论,即使該名字從不被引用,例如mysql就是這樣(因為筆者就是用的MySQL[捂臉])棒呛。當我們使用了from子句的子查詢后聂示,having就顯得不必要,因為having子句使用的謂詞出現(xiàn)在外層查詢的where子句中簇秒,當然鱼喉,不是說不可以用。

對于下面的例子:“找出在所有系中工資總額最大的系趋观,以及總額是多少”扛禽,不使用from子句的子查詢,having子句是無能為力拆内,但用from子句中的子查詢卻能輕易實現(xiàn)旋圆。

#按照系名分組,計算每個分組的工資總額麸恍,從中挑取最大值灵巧。
SELECT max(tol_salary)
FROM (
  SELECT dept_name,sum(salary) AS tol_salary
  FROM instructor
  GROUP BY dept_name
)AS dept_tol;

6、with子句

with子句提供定義臨時關(guān)系的方法抹沪,這個定義只對包含with子句的查詢有效刻肄。例如,找出具有最大預算的系融欧。

#我感覺有點懷疑人生敏弃,因為mysql竟然不支持with子句,不過還是寫一下SQL語句的吧
with max_budget(value) as
    (select max(budget)
      from department)
select budget 
from department,max_budget
where department.budget = max_budget.budget;

with子句最主要的作用是是的邏輯更清晰噪馏,我們也是可以用from子句或者where子句中的嵌套子查詢實現(xiàn)with子句的麦到,只不過看起來很繁瑣绿饵,難懂。
利用嵌套子查詢實現(xiàn)上面句子是這樣的:

SELECT budget
FROM department,(
  SELECT max(budget) as val
  FROM department
  )AS max_budget
WHERE department.budget = max_budget.val;

6瓶颠、標量子查詢

SQL允許子查詢出現(xiàn)在單個值得表達式能夠出現(xiàn)的任何地方拟赊,只要該子查詢只返回包含單個屬性的單個元組,這就叫做標量子查詢:
示例:‘列出所有的系以及它們擁有的教師’

#該嵌套子查詢的結(jié)果只有一行一列粹淋,也就是只返回包含單個屬性的單個元組吸祟。
SELECT dept_name,(
    SELECT count(*)
    FROM instructor
    WHERE department.dept_name = instructor.dept_name
)AS num_instr
FROM department;

附件
主碼用下劃線標注,外碼依賴用從參照關(guān)系的外碼屬性到被參照的主碼屬性之間的箭頭表示

數(shù)據(jù)庫的模式圖

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末桃移,一起剝皮案震驚了整個濱河市屋匕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌借杰,老刑警劉巖过吻,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蔗衡,居然都是意外死亡疮装,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門粘都,熙熙樓的掌柜王于貴愁眉苦臉地迎上來廓推,“玉大人,你說我怎么就攤上這事翩隧》梗” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵堆生,是天一觀的道長专缠。 經(jīng)常有香客問我,道長淑仆,這世上最難降的妖魔是什么涝婉? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮蔗怠,結(jié)果婚禮上墩弯,老公的妹妹穿的比我還像新娘。我一直安慰自己寞射,他們只是感情好渔工,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著桥温,像睡著了一般引矩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天旺韭,我揣著相機與錄音氛谜,去河邊找鬼。 笑死区端,一個胖子當著我的面吹牛混蔼,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播珊燎,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼遵湖!你這毒婦竟也來了悔政?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤延旧,失蹤者是張志新(化名)和其女友劉穎谋国,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體迁沫,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡芦瘾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了集畅。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片近弟。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖挺智,靈堂內(nèi)的尸體忽然破棺而出祷愉,到底是詐尸還是另有隱情,我是刑警寧澤赦颇,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布二鳄,位于F島的核電站,受9級特大地震影響媒怯,放射性物質(zhì)發(fā)生泄漏订讼。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一扇苞、第九天 我趴在偏房一處隱蔽的房頂上張望欺殿。 院中可真熱鬧,春花似錦鳖敷、人聲如沸祈餐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽帆阳。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蜒谤,已是汗流浹背山宾。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留鳍徽,地道東北人资锰。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像阶祭,于是被迫代替她去往敵國和親绷杜。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容