編程學習筆記 2023-08-25 #00002 | 主題 : SQL中SubQuery的實用性探討
編程學習筆記 2023-08-25 #00002
主題 : SQL中SubQuery的實用性探討
至初中學習編程開始,一直是以Indie Game Dev作為目標,持續地認為SQL對自己無太大幫助而幾乎都對其都是抱持忽視態度(我後來才知道SQLlite在開發遊戲上也頗有用處的)。
但近一年因為工作關係多接觸了SQL,亦開始有些心得,希望有機會可以分享,之後陸續都會有其他SQL相關的內容。
而最近習慣了用SubQuery處理大部分的SQL編寫,發現非常實用,
決定以SubQuery為第一篇有關SQL的文章。
-SubQuery
SubQuery概念十分簡單,在一個SQL語句中括號裡的Query便是SubQuery。
例子1 : select * from (select * from foo) 中的
(select * from foo) 便是SubQuery。
例子2 : select * , foo=(select top 1 foo from bar) from baz 也是SubQuery。
i.) SubQuery有什麼用?
最普遍對SubQuery的用法應該是上述例子1的用法,把較複雜的Query(如有大量的Join、Group)整合為一個新Table,以便更好地調整Select或Where。
然而,SubQuery其實還有無數的用法,能實現初學者以為無法實現的Query,可謂十分實用的技巧,但可惜的是大多SQL相關教學都不太會在相關內容上着墨,這也是為什麼我希望為這功能特書此文的原因。
i.) 為什麼要使用SubQuery?
1) 快
SubQuery利用得當的話(後文會再提及使用方式),能夠代替其他常見的Function如Join
Table等,而且其效率也更高,例如我在公司的Database進行過測試,在能得到相同結果
的前提下,要在一個有2815680個Rows的Table加入另一個Table的Col的速度分別是,
Left Join的運行時間為3分57秒;
SubQuery的運行時間為2分21秒
***(當然最終的時間還是視情況而定,並不代表使用SubQuery就一定比Left Join快,
只是有多一個選擇一定會比只用Join來的有效率。)
2) 清晰
這一點僅為主觀看法。
以下是於DemoDB查詢foo1表,並加上bar1表的ClientName及bar2表的ClientKey的常見做法。
(希望沒寫錯syntax)
use DemoDB;
select
foo1.*,
ClientName=bar1.ClientName,
ClientKey=bar.ClientKey
from foo1
left join [bar1] --Left Join 1
on foo1.baz = bar1.baz
left join bar2 --Left Join 2
on foo1.baz = bar2.baz
以下是使用SubQuery的做法。
use DemoDB;
select
*,
ClientName=(
select top 1 distinct ClientName from [bar1]
where foo1.baz = bar1.baz --SubQuery 1
),
ClientKey=(
select top 1 distinct ClientKey from [bar2]
where foo1.baz = bar2.baz --SubQuery 2
),
from [foo1]
就上述例子來說,使用Left Join時,實際上Where以及Select所處理的表是由foo1、
bar1及bar2組合的表,會有大量無用的Column,如果只是一至兩層的SQL倒還好。
但一但多幾個Table就十分容易造成混亂。
而用SubQuery處理的話,只需取有用的Column,對其他使用者的可讀性會比較高。
**當然,如果需要從單個表中取得多個Column的話,SubQuery並不會比Join來得快及清晰,
而且要以SubQuery實現Inner/Outer Join的話會十分麻煩,
因此,選擇正確的做法完成目標比什麼都重要,但前提是要了解且認識更多可能的Solution
3) 本文的重點 : 因為它是進階技巧
毫不諱言地說,對SubQuery的靈活使用可以說是新手轉老手的轉職試煉,如果理解了
SubQuery的泛用性的話,能實現不少初心者時期以為無法實現的效果。
如要解釋上文提及的泛用性就要從SubQuery的根本開始說起,因為所謂的SubQuery其實
只不過是把Query的Result打包,並塞到另一個Query當中。
而把Query打包後的結果分別有三種 :
i) 表 , 即Table
一般搭配Exists使用,用於檢查該Row是否在另一Table存在/不存在
可同時檢查多個Column是否相同:
use DemoDB;
select
*
from [foo]
where exists (
select 1 from [bar]
where
[foo].ClientID = [bar].ClientID and
[foo].ClientKey = [bar].ClientKey
...
...
) ii) 列,即只有一個Column 的Table(或可被稱為Lists)
一般搭配in使用,用於檢查該Value是否在另一Table的Column存在/不存在 :
use DemoDB;
select
*
from [foo]
where ClientID in (
select ClienID from [bar]
) iii) 值,即只有一個Column及一個Row的Table(或可被稱為Value)
可以用在Update中,
複製同一Table內有類似Unique Code的Value是我較常有的用法:
use DemoDB;
update foo1
set ClientKey=(select top 1 ClientKey from [foo] foo2 where
foo1.ClientID = foo2.ClientID+'_2')
from [foo] foo1或是用於加入另一Table的Column :
use DemoDB;
select
*,
bar_ClienKey=(
select CientKey from [bar]
where [bar].ClientID = [foo].ClientID) --這是值
from [foo]
當然,以上的用法只是冰山一角,畢竟打包後的SubQuery都只是表、列及值,只要是Syntax上容許使用表、列及值的地方,都可以利用。
多留意可以使用SubQuery的地方,並習慣使用SubQuery之後,
會發現寫SQL Script的過程會更加自由且有趣。