編程學習筆記 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的過程會更加自由且有趣。


Published by