【需求分析】
系统接入ERP,订单会根据商品的发货仓进行拆单,所以订单之间不再是单独的关系,而有了子单母单的概念。在做最小的改动的情况下,数据库的改造是在订单表上增加了parent_order_sn字段,所谓的母订单号。如果订单A被拆单,则A应该是母订单,订单状态是‘已拆单’。通过parent_order_sn=‘A订单号’去查询,可以查到对应的子订单记录。
【问题再现】
之前我一直在负责和ERP系统对接的项目,所以对原系统的订单查询改造是另一个同事写的。最近和ERP的对接做了一些修改,可能母订单A拆除的子订单B和子订单C会再一次进行拆分,即订单B可以是一个母订单,B下面有其他两个子订单D和子订单E。在进行这一轮修改的测试,订单列表查询偶尔出现了错位的情况,如下:
正确的展示,如下:
但这种错误现象也不是百分百存在,之前都没有发现有问题。看别人的代码, 真的是不想做的一件事,特别是这种复杂问题的代码,上线前一天看了下,觉得代码写的太复杂了,找不到问题,还是不动了。而就在昨天上线的上午,出现频率比较高,就乖乖又去看代码了,按照代码实现逻辑理了一遍,发现其实思路就是不对的,分页查询一直是有问题的,也没有人注意到。
【代码重写】
原来的代码实现思路是:将所有的母单排除,查询出所有的子单,并加上分页一页10条。然后循环结果集,从子单上获取相关订单信息组装到母单上。
而这样的结果,就不对了。如果在10条记录中,有子母单的订单,查询到的结果数就一直是不确定的。最简单的设想,前10条都是子母单的订单,那么其实只会展示5个订单,而每页的订单数量是根据子母单数量的多少而改变的,显然是不对的。
既然找到问题了,而且代码实现完全不对,就直接选择换思路重写代码,不改也逃不过了。
现在的代码实现思路是:将所有的母单查询到,查询完母单后,再去循环,判断如果有子单的情况下,去查询订单下所有的子单,组装到母订单上。这样不管有多少子母单的情况,有子单算一单,没有子单也算一单,也不用关心这个母单是不是由一个子单继续拆分的,也保证了分页的数量是正确的。
【实现难题】
既然思路确定了,接下来就是写代码了。但是问题来了,我不知道怎么样把所有的母单查询到,而且是不重复的所有母单。下面是数据库中订单的截图:
总结一下,其实就是两种情况:
- parent_order_sn那一列可能是没有值的,也就是算是一个母订单;
- parent_order_sn有值的情况下,一定是会有多条的。
我怎么保证一个sql能查询到没有问题的分页结果,parent_order_sn有值的情况下,需要Distinct,没有值的情况下,需要查出所有,脑子里开始想SQL怎么写,GROUP BY,DISTINCT,IF,CASE…WHEN…各种SQL函数,还是不知道这个SQL该怎么写了。
中午吃完饭就开始想,到两点要开始工作了,和前端说了句,这一个SQL没法写,写不出来,我只能查出正确的结果之后,再自己去做分页分组的处理。说完,我就想到了既然一个SQL不能实现,我就用两个SQL去查,然后合并结果集。三两分钟,不重复的母单的SQL查询就写出来了。如下:
SELECT
*
FROM
(
SELECT
t.*, cus.customer_title
FROM
wms_orders t
LEFT JOIN wms_customer cus ON cus.id = t.buyer_id
WHERE
t.order_status != 100
AND t.comp_id = 8815
AND t.parent_order_sn IS NOT NULL
AND t.parent_order_sn != ''
GROUP BY
parent_order_sn
ORDER BY
t.id_ DESC
) a
UNION
SELECT
*
FROM
(
SELECT
t.*, cus.customer_title
FROM
wms_orders t
LEFT JOIN wms_customer cus ON cus.id = t.buyer_id
WHERE
t.order_status != 100
AND t.comp_id = 8815
AND (
t.parent_order_sn IS NULL
OR t.parent_order_sn = ''
)
ORDER BY
t.id_ DESC
) b
ORDER BY
id_ DESC
其实很简单,就是两种分开写,用union将两个SQL合并,查询不重复的结果集。
第一个SQL对应parent_order_sn有值的情况下,用Group By按parent_order_sn字段分组,并且,将第二种情况parent_order_sn没有值的排除。
第二个SQL对应parent_order_sn没有值的情况,并且将第一种情况parent_order_sn有值的排除。
最后用union,并且按id倒叙排序。
【心得总结】
- 代码实现前,思路是很重要的,思路是对的,才能保证结果是对的。
同事做之前就和她讨论过该怎么实现,可是结果还是按照她自己的想法去做的,之前没有发现问题,我也就没有去看她是怎么实现的。看完后,才发现思路是不对的,结果就不一定能保证对了。
- 代码重写,双方都不是滋味。
出问题的代码,不是自己写的,一万个不想去看别人写的代码。出问题的代码,是自己写的,结果不对,于是被别人推翻重写,一万个怀疑我写的代码怎么可能不对。讲真,这应该是这行业最真实的心理写照。
- 代码是需要积累的,项目经验是宝贵的。
在工作中,每一个需求,每一次代码实现,都是我们积累的财富。代码写的多了,项目中遇到的情况多了,总是会有价值的。
- 不要总想着逃避,其实没有那么困难。
时间长了,心态不太好了,总想着问题和我无关,少做一点,让自己轻松一点。出问题了,就觉得牵扯的东西太多了,不想去碰,等到不得不解决的那一刻才去做,而不是主动去处理问题。换一个心态,或许结果还是美好的。