上一篇我們用子查詢解決了複雜的需求。
功能是做出來了,但你有沒有發現,那個 SQL 看起來很恐怖?
一層包一層,光是要搞清楚哪個括號對應哪個括號,就讓人頭很痛。
這篇來聊聊,為什麼這種寫法會造成問題,以及我們該怎麼思考這件事。
上一篇的巢狀結構
還記得上一篇的完整 SQL 嗎?
SELECT 三年級自然科班級.班級ID
FROM (
SELECT 授課班級.班級ID, 課程.學生上限
FROM 授課班級
LEFT JOIN 課程 ON 授課班級.課程ID = 課程.課程ID
WHERE 課程.年級 = 3 AND 課程.科目 = '自然'
) AS 三年級自然科班級
LEFT JOIN (
SELECT 授課班級.班級ID, COUNT(選課.選課ID) AS 學生人數
FROM 授課班級
LEFT JOIN 選課 ON 授課班級.班級ID = 選課.班級ID
GROUP BY 授課班級.班級ID
) AS 班級學生人數
USING (班級ID)
WHERE 學生人數 < 學生上限看到這坨東西,你大概會想:「天啊,這到底在寫什麼?」
但其實,這個查詢的主結構很簡單。
我們把子查詢的部分先用名字代替:
SELECT 三年級自然科班級.班級ID
FROM 三年級自然科班級
LEFT JOIN 班級學生人數 USING (班級ID)
WHERE 學生人數 < 學生上限就是把兩個結果合併,然後篩選。
問題是,我們把子查詢塞進 FROM 和 LEFT JOIN 裡面,整個結構就變得很難讀。
巢狀結構的三個問題
難讀:腦袋要同時記住太多東西
當你讀到 FROM 後面,發現是一段很長的子查詢。
你還沒消化完,又讀到 LEFT JOIN 後面,又是另一段子查詢。
你的腦袋要同時記住兩個複雜的東西,然後再理解它們怎麼合併。
這對人類來說太難了。
電腦不會有這個問題,它就是一行一行執行。
但人類讀程式的時候,腦袋的容量是有限的。
難維護:兩個月後還要能看懂
寫程式不是只有「功能能跑」就好。
你寫的東西,未來你自己要看,你的同事也要看。
想像一下,兩個月後你回來看這段程式。
你可能是要修一個 bug,或是要加一個新功能。
結果你打開程式碼,看到這段巢狀結構,你得從頭理解一次。
這幾乎等於重寫。
很多人以為寫程式是一個人的事。
但實際上,寫程式更像是集體創作。
你會跟團隊的夥伴一起寫,你寫的東西別人要能看懂。
就算是你自己,兩個月後的你,其實也算是「另一個人」了。
所以「意圖性」很重要——讓人一眼就看懂你想做什麼。
難改:複製貼上容易漏改
巢狀結構還有另一個問題:不好改。
假設你有兩個地方都用到類似的子查詢,你可能會複製貼上。
結果要改的時候,你要改兩個地方。
更慘的是,你可能改了一邊,忘了改另一邊,造成結果不一致。
這種 bug 很難發現。
用變數的概念來思考
如果你有寫過其他程式語言,你應該知道「變數」這個東西。
x = 10
y = 20
z = x + y你可以把一個值存進變數,之後用變數的名字來引用它。
變數還有一個好處:你可以把複雜的運算結果存起來,取一個好懂的名字。
月營收 = 計算這個月的營收()
月成本 = 計算這個月的成本()
月利潤 = 月營收 - 月成本這樣讀起來就很清楚:月利潤等於月營收減掉月成本。
你不用去看「計算這個月的營收」裡面到底做了什麼複雜的運算,你只要知道它的結果叫做「月營收」就好。
回到 SQL,如果我們也能這樣做就好了。
還記得上一篇的兩個子查詢嗎?
第一個子查詢:查出三年級自然科的班級
SELECT 授課班級.班級ID, 課程.學生上限
FROM 授課班級
LEFT JOIN 課程 ON 授課班級.課程ID = 課程.課程ID
WHERE 課程.年級 = 3 AND 課程.科目 = '自然'第二個子查詢:算出每個班級的學生人數
SELECT 授課班級.班級ID, COUNT(選課.選課ID) AS 學生人數
FROM 授課班級
LEFT JOIN 選課 ON 授課班級.班級ID = 選課.班級ID
GROUP BY 授課班級.班級ID如果我們能把這兩段子查詢存起來,給它們取個名字:
三年級自然科班級 = 第一個子查詢的結果
班級學生人數 = 第二個子查詢的結果那主查詢就可以直接用這些名字:
從 三年級自然科班級 LEFT JOIN 班級學生人數 ...這樣讀起來就清楚多了。
你可以先看主查詢的結構,知道大概在做什麼。
想看細節的時候,再去看各個「變數」是怎麼算出來的。
SQL 裡面有沒有類似的做法呢?
有的,下一篇會介紹。
小結
這篇聊了巢狀查詢的三個問題:
難讀:
你的腦袋要同時記住多個複雜的子查詢,然後再理解它們怎麼組合。
難維護:
寫程式不是只有當下能跑就好,未來你自己和同事都要能看懂。
難改:
複製貼上的子查詢,改的時候容易漏改,造成結果不一致。
下一篇會介紹 SQL 裡面類似「變數」的做法,讓你把子查詢獨立出來,變得好讀好改。