很早的一個問題了,今天碰到又繞了一下。其實很簡單。
關係模式:
學生(學號,姓名,系別,年齡)
課程(課程號,課程名,學時)
選讀(學號,課程號,成績)
問題:檢索選讀全部課程的學生姓名
select 學生.姓名 from 學生
where not exists(
select * from 課程 where not exists(
select * from 選讀 where 學號=學生.學號 and 課程號=課程.課程號))
意思是
con1:這個學生不會con2。
con2:他有課沒選。
下面是網上關於這件事的討論,弄個合集防止腦子再發懵。。。
現在我們從後面的子查詢向前分解:
1.所有未選過的課程的資料集:
select * from 課程 where not exists(
select * from 選讀 where 課程號=課程.課程號)
2.所有沒被某位學號為 @學號 的學生選過的課程的記錄集(@學號學生的未選課程):
select * from 課程 where not exists(
select * from 選讀 where 學號=@學號 and 課程號=課程.課程號)
請注意,多出了學號的篩選即,學號=@學號。
3.遍曆每一個主查詢的學號,每一個學號都按第二篩選方法篩選出:沒有未選課程的學生的學號。(不包括在第查詢方法查詢出的“有未學課程的學號的記錄集”中的記錄。)
請注意:用主查詢中的 學生.學號 代替了@學號。
select 學生.姓名 from 學生
where not exists(
select * from 課程 where not exists(
select * from 選讀 where 學號=學生.學號 and 課程號=課程.課程號))
這已經是整個查詢語句,可以看出子查詢中用學生.學號替換了第2步中的 @學號,(沒忘了吧,第二步求的是沒有選修全部課程的某個學生)。
用上面的講解方法不知道是不是更好理解一點。
===============
針對您最後的補充:
為什麼不用
from 學生,課程,選讀?
答:因為from 學生,課程,選讀 也就是
SELECT * FROM 學生,課程,選讀 WHERE .....條件
這樣的型式只能組成內聯結,而內聯結我們只知道,只有三個表按聯結條件都符合才會被選中,而我們要的是有不符合後面兩表的的學生才符合條件,所以用內聯結是不可行的。
要不然where句怎麼可以調用三個關係的屬性呢?
答:在SQL SERVER中規定:所有子表都是可以通過表名調用其上面任意層次的表中的欄位的。所以,不是只有內聯結才可以調用多個表的屬性。
雙重否定
從最裡面開始執行
select * from 課程 where not exists(
select * from 選讀 where 學號=學生.學號 and 課程號=課程.課程號))
這段是返回某學生沒有選擇的課程,假設返回集a
前面套個not exists的後,
當a有值,即,某學生存在沒有選擇的課程時,整段sql文返回null,
在這裡http://www.blogjava.net/decode360/archive/2009/06/12/282804.html也有解釋。
當a無值,即,某學生不存在沒有選擇的課程時,就是說選擇了全部課程,整段sql文該學生返回姓名