1、場景需求
一個數據庫表的更新,需要聯動引起另一個表的數據更新,這在很多應用場景下都需要。比如:
有1個訂單表,其中有訂單金額、已收總金額、剩餘應付、成本總支出、毛利潤5個字段;另外有1張成本支出記錄表、1張收款記錄表,這2張表都有1個OrderId字段,作爲外鍵來關聯訂單表。業務效果,如下圖所示。
現在增刪改成本支出記錄表、收款記錄表,都需要同步更新訂單表中相關金額。
這種需求,我們一般有2種方法來解決。
1)不採用表字段存儲總支出和總收入,而採用SQL的sum函數,在訂單列表時,實時統計查詢出數據。
優點:無需字段存儲,也不用考慮總計數據的聯動更新。
缺點:數據統計消耗性能,還需要進行多表連接查詢。
2)採用表字段存儲總支出和總收入。
優點:直接從表字段中取數據即可,性能高。
缺點:需要考慮各種能影響到數據變動的情況。稍有不周,易引起數據更新遺漏。
我們採用第2種方式來實現,以減小SQL查詢統計的複雜度。
2、選定方案
採用第2種方案,需要考慮到各種引起數據變動的情況。對於上述案例,無非就是單條支出的變動,會引起總支出的變動。而單條支出的變動,無非就是增刪改。所以在業務邏輯中,盯住增刪改就可以了。
使用太極平臺,我們有2種方式去實現。
1)單獨指定增刪改的action地址,在服務器後端進行編碼處理。見章節:使用自定義action來獨立處理增刪改操作。
這種方式可行,但是違背了我們使用太極平臺的初衷。使用太極平臺,就是要少編碼,甚至是不編碼。所以我們不採用此方案。
2)不做任何配置變動,採用MySQL的觸發器進行實現。這也是本文重點要說明的方式。
接下來,將說明使用觸發器的方式如何實現。
3、數據表結構
先將3張表的詳細結構進行說明。爲簡單描述,刪除了不必要的字段。表設計如下:
1)訂單表(qd_order)
2)訂單支出表(qd_order_cost)
3)訂單收款表(qd_order_payee)
4)3個表關係的E-R圖,如下圖所示。
4、觸發器實現方案
觸發器是數據庫層的機制,因此與業務代碼無關。觸發器與存儲過程類似,都在數據庫上進行編寫。如果使用的是阿里雲的DMS平臺,那麼可以在這裏看到。如下圖所示,是我們編寫好的觸發器。
我們現在要實現這樣的效果:
1)增刪改支出記錄,同步更新訂單表總支出、毛利潤;
2)增刪改收款記錄,同步更新訂單表總收款、毛利潤;
3)修改訂單表的訂單金額,同步更新剩餘應付款;
觸發器的使用方法,可以自行去網上學習。我們以支出記錄爲例,進行詳細邏輯說明。
如下圖所示,是添加支出記錄時的觸發器語句。在完成支出記錄插入後,更新訂單表的總支出、毛利潤。這裏的毛利潤=總收款-總支出。
可以看到,編寫觸發器,就是編寫SQL語句。只是要注意觸發時機,是在after還是before,多實踐操作就積累經驗了。
5、觸發器的SQL語句代碼
8個觸發器的SQL語句如下。最後2個是訂單表自身的,語句稍微和其他的不一樣,因爲是觸發自身,同時更新自身,所以只需要set就可以了。
1)支出表qd_order_cost添加記錄時的觸發器:OrderCost_After_Insert
begin
/**添加成本支出時,更新訂單的總成本與毛利潤**/
update qd_order set TotalCost=(select sum(CostMoney) from qd_order_cost where OrderId=NEW.OrderId),
GrossProfit=OrderPaid-TotalCost where Id=NEW.OrderId;
end
2)支出表qd_order_cost更新記錄時的觸發器:OrderCost_After_Update
begin
/**更新成本支出時,更新訂單的總成本與毛利潤**/
update qd_order set TotalCost=(select sum(CostMoney) from qd_order_cost where OrderId=NEW.OrderId),
GrossProfit=OrderPaid-TotalCost where Id=NEW.OrderId;
end
3)支出表qd_order_cost更新記錄時的觸發器:OrderCost_After_Delete
注意:刪除數據後,有可能相關數據一條都沒有了。那麼sum出來的結果會爲null,而不是0。所以我們需要特殊對待,對sum的結果要進行IFNULL的判斷。IFNULL(SUM(字段),0),這樣就可以在一條數據都沒有的時候,仍然更新0進去。否則爲null就不會執行更新。
begin
/**刪除成本支出時,更新訂單的總成本與毛利潤**/
update qd_order set TotalCost=(select IFNULL(SUM(CostMoney),0) from qd_order_cost where OrderId=OLD.OrderId),
GrossProfit=OrderPaid-TotalCost where Id=OLD.OrderId;
end
4)支出表qd_order_cost更新記錄時的觸發器:OrderPayee_After_Insert
begin
/**添加收款記錄時,更新訂單已收款總額、剩餘金額、毛利潤**/
update qd_order set OrderPaid=(select SUM(PayMoney) from qd_order_payee where OrderId=NEW.OrderId),
OrderRemain=OrderAmount-OrderPaid,GrossProfit=OrderPaid-TotalCost where Id=NEW.OrderId;
end
5)支出表qd_order_cost更新記錄時的觸發器:OrderPayee_After_Update
begin
/**更新收款記錄時,更新訂單已收款總額、剩餘金額、毛利潤**/
update qd_order set OrderPaid=(select sum(PayMoney) from qd_order_payee where OrderId=NEW.OrderId),
OrderRemain=OrderAmount-OrderPaid,GrossProfit=OrderPaid-TotalCost where Id=NEW.OrderId;
end
6)支出表qd_order_cost更新記錄時的觸發器:OrderPayee_After_Delete
begin
/**刪除收款記錄時,更新訂單已收款總額、剩餘金額、毛利潤**/
update qd_order set OrderPaid=(select IFNULL(SUM(PayMoney),0) from qd_order_payee where OrderId=OLD.OrderId),
OrderRemain=OrderAmount-OrderPaid,GrossProfit=OrderPaid-TotalCost where Id=OLD.OrderId;
end
7)支出表qd_order_cost更新記錄時的觸發器:Order_Before_Insert
begin
/**添加訂單時,更新剩餘應付金額,等於訂單金額**/
set NEW.OrderRemain=NEW.OrderAmount;
end
8)支出表qd_order_cost更新記錄時的觸發器:Order_Before_Update
begin
/**訂單總金額更新時,更新剩餘金額**/
if NEW.OrderAmount!=OLD.OrderAmount then
set NEW.OrderRemain=NEW.OrderAmount-NEW.OrderPaid;
end if;
end
6、詳細代碼案例
詳細的代碼與案例,到時候下載演示項目和數據庫,就可以看到了。