這是SHU數據庫原理上機題目中的一道。全部題目:http://www.docin.com/p-739281393.html
代碼網上有:
select xh,xm
from s
where not exists
(select * from c
where not exists
(select * from e
where xh=s.xh and kh=c.kh
)
)
其中s是學生表,c是課程表,e是選課表
上課沒注意聽,實驗驗收前看了很久沒看懂。網上的講解沒看明白,並且還有錯的講解(該頁的最后會有)。直接跟老師說這個不懂,老師讓我中午去找他。花了20多分鍾,我懂了。老師說上課還是要聽,我深以為然。
這代碼有三種方式去理解:
1、離散數學中的謂詞邏輯演算系統
先來復習一下
∃x∀yR(xy) :存在x對於任意的y滿足關系R(xy)。
﹁∀yW(y):不是所有的y都滿足關系W(y),那么就是存在y不滿足關系R,即∃y﹁W(y)
令f(x):x是學生;g(y):y是課程;r(xy):學生x選修了課程y。
“所有課程都選修的學生”可以表示為∃x∈f(x)∧∀y∈g(y)∧r(xy) ,就是存在學生任意一門課都選了。
SQL語言中沒有全稱量詞,所以通過雙重否定來實現
﹁﹁((∃x∈f(x)∧∀y∈g(y))∧r(xy)) => ∃x∈f(x)∧﹁(∃y∈g(y)∧﹁r(xy))
對於﹁∀yR(xy),理解為不是所有的y都滿足關系R,那么就是存在y不滿足關系R,∃y﹁R(xy)
然后用這個式子與上面的代碼對照。
﹁就相當於一個not exists,∃其實相當於是select。
select xh,xm from s 等價於∃x∈f(x)
select * from c 等價於 ∃y∈g(y)
where not exists(select * from ewhere xh=s.xh and kh=c.kh) 等價於 ﹁r(xy)
2、用C++的編程思想來理解
for(int i=0;i<s.length;i++)
{
for(int j=0;j<c.length;j++)
{
if(學生i沒有選修j課程) break;
}
if(j==c.length) i就是選修了所有課程的學生;
}
兩個not exists可以這樣解釋,依次從學生表里找一個學生,讓這個學生去選課表里找課程。如果遍歷到某課程該學生沒有選修,就結束,然后開始遍歷下一個學生。如果是遍歷完課表的話,就代表着該學生學修了所有的課程。
也就是說我們不去用語言來翻譯代碼,換種思維來理解它。
3、純粹記憶
如果查詢“全部”內容,需用到全稱量詞,但SQL語言只提供存在量詞,我們用存在量詞雙重否定來解決
SELECT <查詢內容> FROM 表1 WHERE
NOT EXISTS(SELECT * FROM 表2 WHERE
NOT EXISTS(SELEST * FROM 表3 WHERE
表3與表1聯接條件 AND 表3與表2聯接條件))
表1是查詢結果所需要的表
表2是全部內容所在的表
表3是將聯接上述兩表的表
例:1.查詢使用了全部零件的工程名稱(理解為沒有零件是不用的)
2.查詢至少用了供應商S1所供應的的全部零件的工程號
這種方法是不去理解no exists的原理,記住格式就好了。應付考試專用。
關於上面提到的錯誤的講解:
有人說代碼可以這樣寫:
select xh,xm
from s
where xh in
(
select xh from e group by xh
having count(*) = (select count(*) from c)
)
這樣理解的,統計課程的數量,記為n,假如某學生所修的課程的數量為n。那么該學生就是選修所有課程的人。
剛開始我也以為這樣可以的。因為邏輯上是正確的。
我在課程表c中添加了幾組數據,使得有人選修了所有課號的課。但是運行最上面的代碼(代碼1)和這條代碼(代碼2)的運行結果是不同。
那么導致結果不同的原因是什么呢?課程表c里有重修課程,也就是有課程雖然是相同的課程號,但是由於開課的時間不同,所以它們不算一門課。
那么上面的代碼有可取的地方么?有,特殊的情況是可以用的,就是事件是唯一的的時候。