sql查詢之雙重not exists實現關系代數除運算


sql查詢之雙重not exists實現關系代數除運算

使用數據表

​ 成績信息表:

課程信息表:

問題:

  1. 查詢選修了1,2號課程的學生學號

  2. 查詢選修了全部課程的學生

1.
先上個代碼:
/*1. 查詢選修了1,2號課程的學生學號(因為這篇筆記主要是說明除運算的實現,其他方式解決這個問題的就略過了)*/
select distinct s1.學號
from 成績信息 s1
where not exists(
	select *
	from tmp s2
	where not exists(
		select * 
		from 成績信息 s3
		where s3.學號 = s1.學號 and s3.課程序號 = s2.課程序號
	) 
)

-- tmp是一個視圖,下面是創建語句
go
create view tmp
as 
select 課程序號
from 課程信息
where 課程序號 in ('1', '2');
go
select * from tmp;

先來看看這個視圖,得到的結果如下:

很明顯,這只是為了創建一個只含有1,2課程號的表,當然可以通過其他方式創建,比如直接創建一個表,然后插入數據

接着看看這個查詢,這里用到了兩重not exists,

第一重是對成績信息表進行的掃描,select 可以列舉成對表的每一個元組的枚舉,對於not exists可以理解成exists的結果取反,即true變false, false變true, 還有即使別名s1是為了方便寫起的

然后就是第二層not exists,這個是對創建的那個視圖的掃描。也就是在第一重掃描時,沒掃描一個元組,就會進入第二層的掃描,即第一層每次執行一次,就會執行一次這個代碼

select *
	from tmp s2
	.../*后面第二個not exists先忽略*/

掃描過程就是類似圖中這樣,類似於for循環

再將第二個掃描的not exists那部分加進來

select *
	from tmp s2
	where not exists(
		select * 
		from 成績信息 s3
		where s3.學號 = s1.學號 and s3.課程序號 = s2.課程序號
	) 

加了條件之后對於tmp的掃描進行了一個篩選,

第二重not exists中的條件很好理解,就是拿s2(tmp視圖)中的課程序號, s1 (成績信息表)的學號 去s3中做一個篩選, 當s1中的學號和s2中的課程序號在s3表中找得到時, 第二重not exists中的條件就會返回的是true, 經過第二重的not exists之后就是false,那么對第二重掃描(也就是第二個select)掃描到的這個結果就會被拋棄,然后就是繼續第二層掃描(select)。如果在第二層掃描中的tmp掃描完了,其中的not exists之后的每一個條件都是false,那么第二層掃描中tmp的所有的元組都被丟棄,也就是第二層掃描的結果為空,這個時候第一層掃描的條件經過not exists就會是true。因為exists對於他其中的條件得到的結果為空,那么exists之后就是false,再經過not之后就是true了。然后第一層繼續往下,又重復上面這個過程。

這里補充貼個第二重not exists中的sql語句執行圖

這個條件就是一個自然連接。

下面就來詳細說明這個語句執行的每一步

  1. 第一層掃描(即第一個select那), 拿着s1的第一個元組(1300310101,1,92),

    然后到第一層的not exists那,進入第二層掃描,

    第二層掃描掃描第一個(如下圖), s1中的學號和s2中的課號作為條件在s3中可以找得到元組

那么第二重not exists返回的結果就是false(找得到exists返回true,not 取反), 也就是說第二層掃描得到得結果到現在為空,

第二層掃描還沒有結束,繼續,如下圖,同樣第二層掃描得條件執行后還是找得到結果,那么第二層的not exists條件返回為false,所以 第二層掃描到現在還是空

第二層掃描到現在已經結束了,因為s2(tmp表)到現在已經掃完了,並且第二層掃描的結果為空,第二層的掃描結果作為第一層的判斷條件,為空經過not exists之后就是true了,也就是第一層掃描第一個元組時被留下來了(因為條件為true),那么到現在為止,s1的第一個元組就被保留到最終結果了。

  1. 現在就到了第一層掃描的第二個元組了,也就是(1300310101, 2, 95), 同樣執行第一層的條件,進入到了第二層的掃描,和上邊過程類似,貼個圖應該能看得懂

    進入第二層的掃描之后,執行第二層的條件:在s3中找。這個過程和第一層掃描第一個元組時一樣,第二層掃描中的條件經過not exists之后返回的都是false,

    那么第一層的條件經過not exists之后就是true了,s1表的第二個元組留了下來

  2. 現在到第一層掃描的第三個元組,(1300310101, 5, 88),同樣執行第一層掃描的條件進入第二層掃描,過程

    同樣,第二層掃描中的每一個元組都被丟棄,(因為和上面一樣,第二層掃描中的where條件經過not exists之后返回的都是false),那么第一層條件就是true,s1表第三個元組也被保存下來。

  3. 第一層掃描的第四個元組,過程

    第一層掃描進入條件執行第二層掃描,第二層掃描又執行其條件在s3中查找,

    第二層掃描到第一個元組s2中的課程序號為1的元組,帶入執行條件,學號為1300330102,課號為1的元組找不到,也就是第二層掃描not exists之后返回true, 那么第二層掃描中的第一個元組留了下來。

    其實這個時候已經可以知道第一層循環的not exists條件返回的是true了(dbms也不會再往下執行完整個第二層掃描),因為第一層not exists中的查詢查找到了結果,exists返回的就是true,取反之后就是false , 也就是第一層掃描的第4個元組被丟棄了。

  4. 同理,第一層掃描的第五個元組(太懶了,這個過程就省略了),也被留了下來

  5. 第六個元組也被留了下來。那么總共留下了5個元組。

    可以看看沒有去重的結果:

    去個重就可以得出符合的學號了。

    過程講完了,再來看看這個過程做了什么。

    首先掃描成績信息表(s1), 然后拿着s1的學號,

    通過嵌套子查詢去找tmp表(s2表,存放一號課程和2號課程),

    然后又經過一個嵌套,拿着s1的學號,和1號課程,2號課程分別去成績信息表(s3)中查,看看有沒有這個學號是s1(成績信息表)中的,課號是s2(tmp表)中的。如果第二層掃描能得到0個結果,也就是意味學號為s1的學生對於s2表中的課號都選了,因為第二重not exists中的查詢都能返回結果,經過not exists之后就都是false了,然后第二層掃描(即對s2)的掃描的每一個元組都丟棄,也就是第二層掃描得到0個結果了。然后經過第一層的not exists之后就是true了,那么這個選了s2表中的所有課程的學生就被留了下來了。然后掃描完第一層就能選出所有學生中選修了s2中所有課程的學生了。

    雖然感覺說的也不怎么明白,但大概就是這樣了。也許你可以將問題這么理解:

    查詢成績信息表中的學生中除去只選了1,2號課程中的一部分或者沒選的,這樣一來剩下的就是選了1,2號課的學生了。然后雙重not exists就可以篩選掉那些只選一部分課程的學生了。

    明白了上面這個,第二問就沒什么難度了,只是將s2表換成包含所有課程的表而已。

    這里只給出代碼,不再重復說了,這里和上面不同的是s2直接通過子查詢來構建,對於我這的表,這個查詢是沒有符合的結果的

    select distinct s1.學號
    from 成績信息 s1
    where not exists(
    	select * 
    	from (select distinct 課程序號 from 課程信息) as s2(課程序號) 
    	where not exists(
    		select * 
    		from 成績信息 s3
    		where s1.學號 = s3.學號 and s2.課程序號 = s3.課程序號
    	)
    )
    

漸漸的忘記標題,補充一下關系代數中的除運算

這里只給出一個例子,具體定義怎么下的我也忘了。。。

簡單說就是拿着sc表中的非cno屬性去看它所對應的所有cno屬性,然后看看這些屬性是否包含了C表中的所有,如果包含了,就把這個屬性值留下來。也就是一種包含關系。

而上邊的雙重not exists就是實現了這么一個除法關系,我的理解這個也是實現了包含關系,

再補充一下這樣寫行不行?

select * from sc where exists (
	select *
	from C
	where exist(
		select *
		from sc
		where sc.cno = c.cno
	)
)

之所以這樣的思路是:

在C表中進行一次掃描,看看sc表中的cno是否包含了C表中的cno,如果包含了,就把這個元組留下。

但實際上這樣是不行了,因為只要c表中的任何一個包含在了sc中,那么exists返回就是true,然后sc表中這個元組就被留下來了。

所以直接用肯定的寫法行不通,那就來雙重否定not exists了。

大概就這么多了,再寫我就得吐了,這要是復習還看不明白就砸手機了。

收工。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM