以初學者的角度理解:SQL實現關系除法


以初學者的角度理解:SQL實現關系除法

相信各位在學習SQL的時候,由於沒有一家SQL語言提供除法命令而只能自己寫一個。而網上大多就是四步驟加一個模板:

select distinct	A.X
from			A A1
where			not exists(
	select			B.Y
	from			B
	where			not exists(
		select			*
		from			A A2
		where			A1.X = A2.X
		and			A2.Y = B.Y
	)
)

那四個步驟又寫的過於抽象~,看得一頭霧水。因此筆者希望從一個初學者的角度,講解一下關系除法的實現過程,幫助大家理解。

例子

我們舉一個實例來講解~

我們用一張SC表和Course表,其中:

SC表:

image-20200427075007807

Course表:

image-20200427074822009

而我們想要做的是:

查詢選修了全部課程的學生姓名。

很明顯我們需要做除法:

select distinct	SC1.Sno
from			SC SC1
where			not exists(
	select			Course.Cno
	from			Course
	where			not exists(
		select			*
		from			SC SC2
		where			SC1.Sno = SC2.Sno
		and			SC2.Cno = Course.Cno
	)
)

接下來講解這段語句的整個過程~

具體講解

我們先看看這兩句SQL

select distinct	SC1.Sno
from		SC SC1

這一段,產生的結果應該是:

image-20200427075354250

接下來到下一句:

where			not exists()

這句話的意思很簡單,就是我即將進行括號中寫好的查詢語句,如果查詢結果為空,返回true,否則返回false

這時候,我們開始where語句,此時SC1.Sno的值會被挨個放進where中進行處理。

此時:

未命名筆記 - 2020年4月27日 上午8.00 - 頁面1

接下來進入not exists的部分。

select			Course.Cno
from			Course

跟上面一樣,先看看它執行出來什么效果:

image-20200427080509102

接下來到下一句:

where			not exists()

此時:

13456789

接下來進入下一個not exists的部分。

select			*
from			SC SC2
where			SC1.Sno = SC2.Sno
and			SC2.Cno = Course.Cno

我們的SC表是這樣子的:

image-20200427075007807

我們看看當select完之后,where在干嘛:

where			SC1.Sno = SC2.Sno
and			SC2.Cno = Course.Cno

這句話的意思是:

SC1.Sno與本關系中的Sno相等且Course.Cno與本關系中Cno的值相等

那回頭看看,我們進行的步驟,我們發現到這一步的時候,SC1.Sno = 20110001,而Course.Cno = 001。換言之,我們要找到符合這兩個條件的元組。

不難找到:

未命名筆記 - 2020年4月27日 上午8.21 - 頁面3

因此

select			*
from			SC SC2
where			SC1.Sno = SC2.Sno
and			SC2.Cno = Course.Cno

返回結果集:

20110001 001 89

這時候,我們看回上一層結構:

select			Course.Cno
from			Course
where			not exists(...)

我們成功返回了not exists部分的結果集,因此where處得到的結果是false,因此Course.Cno = 001不被加入這層結構產生的結果集。所以指針往下,Course.Cno的數據變更:

未命名筆記 - 2020年4月27日 上午8.29 - 頁面2

Coures.Cno = 002

於是我們再次進入該語句:

select			*
from			SC SC2
where			SC1.Sno = SC2.Sno
and			SC2.Cno = Course.Cno

此時SC1.Sno = 20110001,而Course.Cno = 002

未命名筆記 - 2020年4月27日 上午8.31 - 頁面3

返回結果集:

20110001 002 78

這時候,我們看回上一層結構:

select			Course.Cno
from			Course
where			not exists(...)

我們成功返回了not exists部分的結果集,因此where處得到的結果是false,因此Course.Cno = 002不被加入這層結構產生的結果集。所以指針往下,Course.Cno的數據變更:

未命名筆記 - 2020年4月27日 上午8.35 - 頁面2

Coures.Cno = 003

於是我們再次進入該語句:

select			*
from			SC SC2
where			SC1.Sno = SC2.Sno
and			SC2.Cno = Course.Cno

此時SC1.Sno = 20110001,而Course.Cno = 003

未命名筆記 - 2020年4月27日 上午8.40 - 頁面3

返回結果集:

20110001 003 89

這時候,我們看回上一層結構:

select			Course.Cno
from			Course
where			not exists(...)

我們成功返回了not exists部分的結果集,因此where處得到的結果是false,因此Course.Cno = 002不被加入這層結構產生的結果集。所以指針往下,Course.Cno的數據變更:未命名筆記 - 2020年4月27日 上午8.41 - 頁面2

Coures.Cno = 004

重復上述的操作,不再贅述。

我們發現,由於20110001這位同學剛剛好都把課選了。每次我們都能從SC表中找到數據,因此

select			Course.Cno
from			Course
where			not exists(...)

該段語句最終的結果是一個空集,沒有一個數據被放進了結果集。

這時候我們再回到上一層結構:

select distinct	SC1.Sno
from		SC SC1
where		not exists(...)

該處的not exists中的查詢語句返回的是空集,所以它返回的是true

也就是說,where處得到的結果是true

也就是說,Sno = 20110001 被加入結果集。

我們繼續分析,想要Sno不被加入結果集:

select			Course.Cno
from			Course
where			not exists(...)

這段語句就需要返回結果集。

也就是說這段語句not exists部分至少有一個返回true

那么這段語句:

select			*
from			SC SC2
where			SC1.Sno = SC2.Sno
and			SC2.Cno = Course.Cno

只要元祖沒有被找到(即有一門課該學生沒有選),它就返回空集。

接下來一系列的連鎖反應:

select			Course.Cno
from			Course
where			not exists(...)

返回一個非空結果集

select distinct	SC1.Sno
from		SC SC1
where		not exists(...)

由於返回的是true,所以該學生的學號不被加入結果集。

總結一下

select distinct	A.X
from			A A1
where			not exists(
	select			B.Y
	from			B
	where			not exists(
		select			*
		from			A A2
		where			A1.X = A2.X
		and			A2.Y = B.Y
	)
)

實際上,直觀來看:

select distinct	A.X
from			A A1
where			not exists()

這一段相當於按X分組。

select			B.Y
from			B
where			not exists(
	select			*
	from			A A2
	where			A1.X = A2.X
	and			A2.Y = B.Y
)

這段就是分組來看,去確定在一組中,是不是有某一個數據沒有沒有在B.Y中,如果有,返回沒有在B.Y中的結果

如果的確有一個數據沒在其中,由於它是嵌套在not exists中的,只要它返回沒有在B.Y中的結果不是空集,就說明該組不合要求,不加入結果集。如果是空集,說明符合要求,加入結果集。

瞎說一下

我知道非常繞,靜下心按流程走一走,還是看不懂可以留言喔~


免責聲明!

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



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