專題:SQL之EXISTS
以下面的供應(yīng)商的解說為例:進(jìn)行說明EXISTS用法
(例一).設(shè)供應(yīng)商供應(yīng)零件的關(guān)系模式為SP(Sno,Pno,Qty),
其中Sno表示供應(yīng)商號,Pno表示零件號,Qty表示零件數(shù)量。整個數(shù)據(jù)庫如下表:
Sno Pno Qty
168 r1 3
168 r2 4
168 r3 7
169 r2 1
169 r3 5
170 r4 8
171 r7 5
172 r2 1
172 r7 3
(A)請問下面的SQL語句返回值是什么?
Select * from SP SPY where exists (select * from SP SPZ where Sno = ‘
解析:在exists 中的子查詢在這個例子中只返回一個值。因為從子查詢中返回的行數(shù)至少有一行exits返回為true,這使得表中的所有記錄都被顯示了出來。
結(jié)果如下:
168 r1 3
168 r2 4
168 r3 7
169 r2 1
169 r3 5
170 r4 8
171 r7 5
172 r2 1
172 r7 3
(B)請問下面的SQL語句返回值是什么?
Select * from SP SPY where exists (select * from SP SPZ where Sno=’
解析:返回與168號供應(yīng)商所提供零件相同編號的情況。
168 r1 3
168 r2 4
168 r3 7
169 r2 1
169 r3 5
172 r2 1
(C)若想得到與168號供應(yīng)上所提供零件相同的全部供應(yīng)商的全部產(chǎn)品情況?
解析:Select * from SP where Sno in (select Sno from SPwhere Pno in (select Pno from SP where Sno=’
或者:Select * from SP SPX where exists (select * from SPSPY where SPY.Sno=’
SQL之EXISTS
for(int i =0; i<EOFout;i++)
{
for(int j = 0 ; j<EOFin;j++)
}
(一)解說
例一 A
Select * fromSP SPY where exists (select * from SP SPZ where Sno = ‘
要將他看成
Select * from SP SPY
where exists (select * from SP SPZ where SPZ .Sno = ‘
按照流程可知:
先去取外層主查詢相關(guān)表SPY的第一個元組(168 r1 3),
再執(zhí)行內(nèi)查詢,將 SPY的第一個元組(168 r1 3)作為一個已知的限制條件帶入;
下面看內(nèi)查詢:
select * from SP SPZ where SPZ .Sno = ‘168’;
發(fā)現(xiàn)內(nèi)查詢 只和子查詢相關(guān)表SPZ有關(guān),
而和SPY的第一個元組(168 r1 3) 沒有任何關(guān)系,也就是說主查詢對子查詢沒有限制。
那么此時若內(nèi)層查詢?yōu)檎妫从薪Y(jié)果時,返回外層表中的第一元 組,
通過已知的整個數(shù)據(jù)庫數(shù)據(jù)發(fā)現(xiàn),內(nèi)查詢語句肯定有值,即exists返回true,
故將外層表中的第一元 組 放入結(jié)果表。
接著取出第二元組,執(zhí)行相同的算法(由于外循環(huán)對內(nèi)循環(huán)沒有影響,而內(nèi)循環(huán)始終為true,所以第二元組 也肯定放到 結(jié)果表中)。
一直到掃描完外層整表 。(發(fā)現(xiàn) 外層表所有記錄 都放入到了 結(jié)果表中)
(二)解說
例一B
Select * from SPSPY where exists (select * from SP SPZ where Sno=’
將之看成
Select * from SPSPY
where exists(select * from SP SPZ
where SPZ .Sno=’
and SPZ.Pno=SPY.Pno);
發(fā)現(xiàn)較之 例一 A 內(nèi)循環(huán)多了一個 限制條件 SPZ.Pno=SPY.Pno,
也就是說 在內(nèi)循環(huán)中 引入了外層主查詢相關(guān)表SPY的零件號Pno 。
將 SPY的第一個元組(168 r1 3)帶入內(nèi)循環(huán)。
由select * from SP SPZ where SPZ .Sno =’168’
知內(nèi)表SPZ查詢到三條記錄
168 r1 3
168 r2 4
168 r3 7
從上面知道 SPZ.Pno的值在集合(r1,r2,r3)中
SPY的第一個元組(168 r1 3)的SPY.Pno=r1,在SPZ.Pno的集合(r1,r2,r3)中,
然后將外層表中的第一元 組 放入結(jié)果表。
SPY的第二個元組(168 r2 4)的SPY.Pno=r2,在SPZ.Pno的集合(r1,r2,r3)中,
然后將外層表中的第二元 組 放入結(jié)果表。
SPY的第三個元組(168 r3 7)的SPY.Pno=r3,在SPZ.Pno的集合(r1,r2,r3)中,
然后將外層表中的第三元 組 放入結(jié)果表。
SPY的第四個元組(169 r2 1)的SPY.Pno=r3,在SPZ.Pno的集合(r1,r2,r3)中,
然后將外層表中的第四元 組 放入結(jié)果表。
SPY的第五個元組(169 r3 5)的SPY.Pno=r3,在SPZ.Pno的集合(r1,r2,r3)中,
然后將外層表中的第五元 組 放入結(jié)果表。
SPY的第六個元組(170 r4 8)的SPY.Pno=r4,不在SPZ.Pno的集合(r1,r2,r3)中, 將外層表中的第六元 組 丟棄。
SPY的第七個元組(171 r7 5)的SPY.Pno=r7,不在SPZ.Pno的集合(r1,r2,r3)中, 將外層表中的第七元 組 丟棄。
SPY的第八個元組(172 r2 1)SPY.Pno=r2,在SPZ.Pno的集合(r1,r2,r3)中,
然后將外層表中的第八元 組 放入結(jié)果表。
SPY的第九個元組(172 r7 3)的SPY.Pno=r7,不在SPZ.Pno的集合(r1,r2,r3)中, 將外層表中的第九元 組 丟棄。
(三)解說
例一 C
若想得到與168號供應(yīng)上所提供零件相同的全部供應(yīng)商的全部產(chǎn)品情況?
1>要想知道全部供應(yīng)商的全部產(chǎn)品,必須知道全部供應(yīng)商的供應(yīng)商號。
2>要想知道全部供應(yīng)商號,必須知道 全部供應(yīng)商的零件號
3>要想知道全部的零件號,必須知道 168號供應(yīng)商的全部零件號。
解析:
Select * from SP where Sno
in
(select Sno from SP where Pno
in
(select Pno from SP where Sno=’
即如下:
Select * from SP SPX where SPX .Sno
in
(select SPY .Sno from SP SPY where SPY .Pno
in
(select SPZ .Pno from SP SPZ where SPZ .Sno=’
然后將外層的限制條件 逐層內(nèi)移,用exists代替in:
1>
Select * from SP SPX where exists
(selectSPY .Sno from SP SPY where SPY.Sno=SPX.Sno
and SPY.Pno in
(select SPZ .Pno from SP SPZ where SPZ .Sno=’
2>
Select * from SP SPX where
exists (select SPY .Sno from SP SPY where SPY.Sno=SPX.Sno
and
exists (select SPZ .Pno from SP SPZ where SPZ.Sno=’168’
and SPZ.Pno=SPY.Pno) );
由于第二個exists(即內(nèi)exists)需要第一個exists中的SPY.Pno
所以要將之改成
Select * from SP SPX where
exists (select * from SP SPY where SPY.Sno=SPX.Sno
and
exists (select * from SP SPZ where SPZ .Sno=’168’
and SPZ.Pno=SPY.Pno) );
3>重新組合后,就如答案所示
Select * from SP SPX where
exists (select * from SP SPY whereSPY.Sno=’
and
exists (select * from SP SPZ where
SPZ.Sno=SPX.Sno
and
SPZ.Pno=SPY.Pno));
第一個 exists存在的意義就是 取 168號供應(yīng)商的全部零件號,作為第二個exists的限制條件。
(例二)下面是另一個例子
1、查詢選修了全部課程的學(xué)生姓名
思路一(in)
select Sname fromstudent
where Sno IN
(select Sno from SC
group by Sno //根據(jù)Sno分組,統(tǒng)計每個
/ /學(xué)生選修了幾門課程。如果等于course的
//總數(shù),就是我們要找的Sno
having count(*) =
(select count(*) from course )
) //統(tǒng)計course中共有幾門課程
思路二(EXISTS)
解二:
select sname from student where
not exists (select *from course where
not exists (select * from sc where
sc.cno = course.cno
and sc.sno=student.sno) )
exists做為where 條件時,是先對where 前的主查詢詢進(jìn)行查詢,然后用主查詢的結(jié)果一個一個的代入exists的查詢進(jìn)行判斷,如果為真則輸出當(dāng)前這一條主查詢的結(jié)果,否則不輸出。
上面這個列子,先查詢出student表的結(jié)果,然后將結(jié)果代入到student.sno然后再查詢出sc表中的結(jié)果,再一條一條的代入,感覺有點像for的嵌套循環(huán),第一輪外循環(huán)中,滿足內(nèi)部的not exists條件的course表中的結(jié)果集被保留,然后再判斷外部的not exists,這次判斷是根據(jù)內(nèi)部Course表中被保留的結(jié)果集的情況,如果結(jié)果集為空,則輸出當(dāng)前這一條S表的結(jié)果集;然后再進(jìn)行第二輪大的循環(huán),即取出student表的第二條結(jié)果代入運算。
以上的sql還可以這樣理解,
最內(nèi)部的
select * from sc where sc.cno = course.cno and sc.sno=student.sno
是查詢出所有已經(jīng)選擇過課程的學(xué)生及相應(yīng)課程,
select * from course where not exists 則是所有沒有被選擇的課程,
在這個基礎(chǔ)上的 select sname from student where not exists
則是選取所有沒有未選擇課程的學(xué)生,即選擇了所有課程的學(xué)員名稱。
select Cname from course
where notexists//找不到的組合,提交course
(select * from SC where course.cno = cno andsno = ''00003'') //在SC中匹配
select Sname from student
where NOTexists //
(select * from course
whereNOT exists //不存在的提
//交給course
(select* from SC where
Sno= student.sno andcno = Course.Cno) // 代//入兩個未知變量
)
select Cname from sc
where cno in
(select cno from coure
group by cno//根據(jù)cno分組,統(tǒng)計每個
/ /課程有幾個學(xué)生選修。如果等于student的
//總數(shù),就是我們要找的Sno
having count(*) = (select count(*) from student )
)
select Cname from sc
where cno in
(select cno from coure
group by cno//根據(jù)cno分組,統(tǒng)計每個
/ /課程有幾個學(xué)生選修。如果等于student的
//總數(shù),就是我們要找的Sno
having count(*) = 0
)
2、查詢至少選修了學(xué)生00002選修的全部課程的學(xué)生號碼
3、 它表示的語義為:不存在這樣的課程y,學(xué)生95002選了,而學(xué)生x沒有選
select distinct sno
from sc scx
where
not exists(--------------不存在如下情況
select *-------------------課程y,學(xué)生95002選了
fromsc scy
wheresno='95002'
and
not exist(-------------------但學(xué)生x 沒有選 課程y
select *
from sc scz
where scz.sno = scx.sno
and scz.cno=scy.cno))
首先我們要了解一下SQL語句的執(zhí)行過程。
SELECT 字段
FROM 表名
WHERE 條件表達(dá)式
那它們是按什么順序執(zhí)行呢?分析器會先看語句的第一個詞,當(dāng)它發(fā)現(xiàn)第一個詞是SELECT關(guān)鍵字的時候,它會跳到FROM關(guān)鍵字,然后通過FROM關(guān)鍵字找到表名并把表裝入內(nèi)存。接著是找WHERE關(guān)鍵字,如果找不到則返回到SELECT找字段解析,如果找到WHERE,則分析其中的條件,完成后再回到SELECT分析字段。最后形成一張我們要的虛表。
其它的先不說了,只說WHERE。
WHERE關(guān)鍵字后面的是條件表達(dá)式。如果學(xué)過C語言等編程語言就會知道,條件表達(dá)式計算完成后,會有一個返回值,即非0或0,非0即為真(true),0即為假(false)。同理WHERE后面的條件也有一個返回值,真或假,來確定接下來執(zhí)不執(zhí)行SELECT。
例:
SELECT *
FROM STUDENT
WHERE SNO = '1';
分析器先找到關(guān)鍵字SELECT,然后跳到FROM關(guān)鍵字將STUDENT表導(dǎo)入內(nèi)存,并通過指針p1找到第一條記錄,接著找到WHERE關(guān)鍵字計算它的條件表達(dá)式,如果為真那么把這條記錄裝到一個虛表當(dāng)中,p1再指向下一條記錄。如果為假那么p1直接指向下一條記錄,而不進(jìn)行其它操作。一直檢索完整個表,關(guān)把虛表返回給用戶。
再說EXISTS謂詞,EXISTS謂詞也是條件表達(dá)式的一部分。當(dāng)然它也有一個返回值(true或false)。
例:
SELECT Sname
FROM Student
WHERE EXISTS
(SELECT *
FROM SC
WHERE SC.Sno = Student.Sno AND SC.Cno = '1');
這是一個SQL語句的嵌套使用,但和上面說的SQL語句的執(zhí)行過程也是相同的。嵌套的意思也就是說當(dāng)分析主SQL語句(外面的那個SELECT,我們權(quán)且先這么叫它)到WHERE關(guān)鍵字的時候,又進(jìn)入了另一個SQL語句中。那么也就是說,分析器先找到表Student并裝入內(nèi)存,一個指針(例如p1)指向Student表中的第一條記錄。然后進(jìn)入WHERE里分析里面的SQL語句,再把SC表裝入內(nèi)存,另一個指針(例如p2)指向SC表中的第一條記錄,分析WHERE后面的條件表達(dá)式,依次進(jìn)行分析,最后分析出一個虛表2,也就變成
SELECT Sname
FROM Student
WHERE EXISTS 虛表2
如果虛表為空表,EXISTS 虛表2 也就為false,不返回到SELECT,而p1指向下一條記錄。如果虛表2不為空也就是有記錄,那么EXISTS 虛表2 為true同,返回到SELECT并把p1指向的記錄添加到主SQL語句的虛表1當(dāng)中。(這也是為什么嵌套的SQL語句SELECT 后面為一般為*的原因,因為它EXISTS返回的只是真或假,字段的名沒有意義,用*就行,當(dāng)然用別的也不會錯。)
注意,這里雖然嵌套的SQL語句分析完了,但主SQL語句只執(zhí)行了一遍,也就是說p1指向Student的第一條記錄,p1還要再指向Student表的下一條記錄并分析,這樣又進(jìn)入了嵌套中的SQL語句,同上面說的一樣分析。當(dāng)p1也到了Student表的結(jié)尾,整個SQL語句結(jié)束。返回虛表1Sname這一列。
嵌套就像:
for(int i = 0,i < n, ++i)
for(int j = 0, j < n, ++j)