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、详细代码案例
详细的代码与案例,到时候下载演示项目和数据库,就可以看到了。