計算邏輯要寫在資料庫(SQL)還是應用程式?
前陣子重構無法除錯的程式時,也把程式傳給新人,把看懂程式當成新人教材來學習。其中一位新人缺少業界開發經驗,目前Java還看得不習慣,週末嘗試用SQL語法把程式邏輯寫出來了。
那段邏輯原本做的事情是用時間區間查詢資料,再根據多個欄位單獨或混合紀錄各類資料數量,輸出到彙整頁面,以及產生excel。
新人寫的SQL在兩table 12萬和15萬的情況下總共處理時間約12秒,與我的版本費時相差不大。透過暫存table和其他功能組成一大串語法回傳一筆資料,各欄位就對應到原本的程式邏輯中的各項計數值。
但是即使效能及格,甚至是其實還不錯,我也不會允許新人在專案程式中使用這種寫法。
把處理邏輯寫在資料庫並非沒有好處,首先最主要的就是減少從資料庫取得的資料量,有時邏輯本身很簡單,但是沒必要的大量資料傳輸會直接導致時間變長。一個好懂的例子是前同事查詢報表資料數量的方式是查詢資料列表再取列表長度,review的時候被主管一句「你為什麼不用select count?」直接論破。
但是想把比較複雜的計算和判斷交給資料庫處理,像是中途需要寫入變數紀錄,或是要建立幾個暫存資料表整理資料後再輸出的情況都建議還是寫在程式裡面,原因如下。
一、分工定義問題
資料庫是用來儲存和取得資料的,應用程式是處理邏輯的,因此把處理資料的工作也交給資料庫處理在分工定義上是有問題的,至少公司內目前使用的SQL語法是盡量維持簡單的做法。
分工明確還有一個好處是容易測試,處理邏輯的函式只要收到符合要求的資料就可以執行,不論是從資料庫取得,還是測試時建立的數筆假資料都行。
二、移植性問題,對資料庫依賴過高
在網路上查過資料庫語法解決問題的人應該都碰過,即使同樣是關聯式資料庫,資料庫語法還是可能有些許不同,有些函式只有MySQL才能使用之類的,如果邏輯處理有用到這類功能,萬一未來要更換使用的資料庫時就需要把所有語法檢查甚至重寫一次。
反之,如果取資料使用的是基本的select、group by等功能,在各資料庫都能使用,不用擔心移植後的衍生問題(至少相對比較少)。
三、不利於測試
用一個函式執行語法得到資料庫回傳的運算結果,這本身就不適合做單元測試,因為這相當於在應用程式中寫了一個很長的函式,直到回傳為止都沒有拆分出子函式簡化邏輯,只能做從頭到尾的整合測試,沒辦法局部驗證處理邏輯。
此外在debug模式執行也沒辦法中途下斷點,確認當下的數值變化,要釐清過程可能還得剪貼語法到資料庫console下執行,容易發生測試過程中修改語法忘記同步回原本程式卻沒發現的情況。
四、缺少執行過程的紀錄(log)
寫在程式迴圈處理資料的時候可以逐一判斷資料,並根據情況印出log,以及使用continue或break等流程控制功能。
資料庫最擅長的就是用語法組合條件篩選資料,雖然也有迴圈語法但是可讀性不如其他程式語言。
以上是我目前對這個議題想到的答案。