1、场景需求

一个数据库表的更新,需要联动引起另一个表的数据更新,这在很多应用场景下都需要。比如:

有1个订单表,其中有订单金额、已收总金额、剩余应付、成本总支出、毛利润5个字段;另外有1张成本支出记录表、1张收款记录表,这2张表都有1个OrderId字段,作为外键来关联订单表。业务效果,如下图所示。

现在增删改成本支出记录表、收款记录表,都需要同步更新订单表中相关金额。

mysql 连表更新不生效 mysql关联表更新数据_MySQL

这种需求,我们一般有2种方法来解决。

1)不采用表字段存储总支出和总收入,而采用SQL的sum函数,在订单列表时,实时统计查询出数据。

优点:无需字段存储,也不用考虑总计数据的联动更新。

缺点:数据统计消耗性能,还需要进行多表连接查询。

2)采用表字段存储总支出和总收入。

优点:直接从表字段中取数据即可,性能高。

缺点:需要考虑各种能影响到数据变动的情况。稍有不周,易引起数据更新遗漏。

我们采用第2种方式来实现,以减小SQL查询统计的复杂度。

2、选定方案

采用第2种方案,需要考虑到各种引起数据变动的情况。对于上述案例,无非就是单条支出的变动,会引起总支出的变动。而单条支出的变动,无非就是增删改。所以在业务逻辑中,盯住增删改就可以了。

使用太极平台,我们有2种方式去实现。

1)单独指定增删改的action地址,在服务器后端进行编码处理。见章节:使用自定义action来独立处理增删改操作。

这种方式可行,但是违背了我们使用太极平台的初衷。使用太极平台,就是要少编码,甚至是不编码。所以我们不采用此方案。

2)不做任何配置变动,采用MySQL的触发器进行实现。这也是本文重点要说明的方式。

接下来,将说明使用触发器的方式如何实现。

3、数据表结构

先将3张表的详细结构进行说明。为简单描述,删除了不必要的字段。表设计如下:

1)订单表(qd_order)

mysql 连表更新不生效 mysql关联表更新数据_联动更新_02

2)订单支出表(qd_order_cost)

mysql 连表更新不生效 mysql关联表更新数据_MySQL_03

3)订单收款表(qd_order_payee)

mysql 连表更新不生效 mysql关联表更新数据_太极平台_04

4)3个表关系的E-R图,如下图所示。

mysql 连表更新不生效 mysql关联表更新数据_太极平台_05

4、触发器实现方案

触发器是数据库层的机制,因此与业务代码无关。触发器与存储过程类似,都在数据库上进行编写。如果使用的是阿里云的DMS平台,那么可以在这里看到。如下图所示,是我们编写好的触发器。

mysql 连表更新不生效 mysql关联表更新数据_联动更新_06

我们现在要实现这样的效果:

1)增删改支出记录,同步更新订单表总支出、毛利润;

2)增删改收款记录,同步更新订单表总收款、毛利润;

3)修改订单表的订单金额,同步更新剩余应付款;

触发器的使用方法,可以自行去网上学习。我们以支出记录为例,进行详细逻辑说明。

如下图所示,是添加支出记录时的触发器语句。在完成支出记录插入后,更新订单表的总支出、毛利润。这里的毛利润=总收款-总支出。

可以看到,编写触发器,就是编写SQL语句。只是要注意触发时机,是在after还是before,多实践操作就积累经验了。

mysql 连表更新不生效 mysql关联表更新数据_MySQL_07

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、详细代码案例

详细的代码与案例,到时候下载演示项目和数据库,就可以看到了。