大佬求助,生成 6000 条数据居然花费 30 分钟?

2020-12-17 16:41:26 +08:00
 xzour

不知不觉,做开发已有 2 年,传统行业的 IT 开发,需求的大概是全能手,SAP 上了一期,对接了 SAP,了解了一些 ABAP 语法,写了几个接口,慢慢的对语言不在执着,对技术也不再执着,因为出身工厂,对工厂业务还算熟悉,对解决方案的思考还算比较热衷。所以就沉浸在计算的一块,但是技术算法认知的先天不足,所以想请大佬把把脉,以后的学习方向。比如以下一题:

  1. 一个财务的一个清账模拟功能
  2. 已知系统目前有几千个客户
  3. 计算周期为一月一次清账
  1. 有 3 个数组,第一个数组是客户组,大概有几千条。第二个数组是各个收款明细,有几千条,不一定全部客户都有,第三个数组是应收发票,大概有几千条,不一定全部客户都有
  2. 需求是每个客户的收款与发票对碰核销,记录每笔核销的记录及每个客户的期末余额。

目前这个模块我是通过暴力遍历实现的,但是速度太慢了,逻辑如下

  1. 遍历每个客户
  2. 读取该客户的收款及发票
  3. 遍历收款,取发票一条一条核销,一条销完,换另一张发票,未销完,记录发票 INDEX 及剩余金额
  4. 最后将结果批量插入数据库。 大概 6000 多条核销明细花了我 30 分钟+ 不可忍受。

因为没到月末,基本收款及发票是不一定真实的,有可能会冲销掉。实时清账就有可能反清账,所以目前清账功能财务需求是预清账,点击就跑,期末定了再关账。

第二个问题是关于动态计算的表达式引擎问题,2W 条数据动态计算,用了 C#的库,居然还要几分钟。 大概需求如下
一个单据,有很多属性,然后不同的属性,会对应不同的成本及利润计算公式。所以我把它放在前端用动态表达式的方式配置,而不是代码写 IF (因为情况大概目前有 70+种,再加以后的新的营销活动啥的,财务提一个需求就要改一次代码太麻烦,所以找了一些动态表达式的库,通过前端配置触发条件) for example.

  1. 某个人(参与 A 活动),提成是,毛利提成》销售额提成,则毛利提成,反之销售额提成
  2. 某个人是负责 A 区域的,结果此张单成交区域在区域外,提成需打折。
  3. 某个活动提成不计成本,直接按某个比例算提成。等等。

1.目前逻辑是,遍历 2W 条,先匹配布尔条件生效判定(可能不止 1 个布尔条件),但终归 70+种情况组组合,最后得到的算法,然后计算提成。

因为用了 C#的表达式引擎,Flee ( Fast Lightweight Expression Evaluator ),目前用时 3~5 分钟,但是这 5 分钟不太适合体感使用,因为在结算的时候,表达式会调整的,然后重算验证就等的不耐烦了。

题外话:公司的技术大佬是中小方案很熟练,BS/CS 端直接上手,打印报表配置,运维都可以,中小企业不可少的人才,但是技术深度不行,喜欢代码一把梭,项目 MVC 还是我来了之后分的,以前直接控制器+静态类打天下,我想了一下我又不想成为绑定公司的人才,所以对多语言的学习比较抗拒,对技术逻辑比较感兴趣,对技术遇到的难题,也喜欢思考,但是非科班,所以很多时候都是暴力算法,然后因公司上市,业务发展的复杂度也越来越高,传统行业对 IT 技术人才是不太尊重,感觉目前的代码不重构,会因为复杂度,迟早黑盒子一样的存在.....

3221 次点击
所在节点    程序员
28 条回复
laminux29
2020-12-18 10:12:08 +08:00
@xzour 我的意思是,只要不在程序里 for 循环然后一条一条去查就行了。存储过程是我建议的方式,你用其他方式也行。总之瓶颈不要被限制在数据库客户端驱动的 rpc call 就行。
jj783850915
2020-12-18 12:26:42 +08:00
善用表驱动 避免非必要的查找
tlday
2020-12-18 12:54:51 +08:00
第一个问题感觉本质上是数据库结构设计有问题,依我对财务极其粗浅的理解,收款和发票理想不该是一对一,最次不该是多对多的关系么。

第二个问题我建议你还是从根本上解决,从后端实现,不知道为什么你觉得改前端代码就不算改代码了...而且前端传表达式这种东西,漏洞,bug 会非常多。我不是很懂计算提成这种东西敢放在前端计算是什么操作。即便只是 bool 条件,也是很容易存在很大漏洞的。更别提如果后面上了权限管理,你可能需要 hack 表达式解析引擎了。我查了一下你说的这个 Flee,298 个 star,上一次提交是二月份合并了两个 pr,再上一次提交已经是去年三月份了,这种不仅用的人少而且 maintainer 不活跃的库真的不建议用在生产环境。
xzour
2020-12-18 13:51:33 +08:00
@tlday 第一个问题:公司的业务收款发票不是一对一,但是给业务员的提成是实时回款结算,回多少结算多少,并且根据产品性质,营销活动,所在区域,下单日期等等落实到具体单据综合结算的。比如一个客户分别在一个月内买了 N 个商品(不同属性),前期预付款 30%,2 个月后付款 70%,这还算比较简单的。你也可以理解为 2W 多条数据,目前有 70 种提成条件这样子。
所以收款明细与发票,最多能保持总额账的话是相等的。但不代表每期是相等的。收款不是一笔收完的。可以理解为多对多关系。
第二条,是因为 C#的解决方案好像是没有 JAVA 解决方案多还是,个人能力有限,不能很好的找到替代方案,之前的公司方案是用 sql 来计算 BOOL 和数值的。公式解放给财务配置,我想这是未来财务自动化一个很大的方向。我是想参考 OA 自动工作流中如何自定义审批流来实现的。(云之家,泛微、企业微信的一些审批第三方件都支持),即拖拉思维导图,写上触发条件,即走哪一条线,(当然这次一条数据),比我 2W 条数据会少很多。体感也不错。
至于 eval 的问题,当然弊端多多,但是既然商业上有这种产品,说明他们是有克服前端传过去执行所产生的的问题。就比如这个 Flee,它分三步,会事先限定每个变量的类型,再渲染公式计算树,最后才计算 bool 或者值,也可以传入自定义的静态方法,反射调用?安全性比我用 sql,用 eval(javascript )安全多了,当然,表达式引擎 JAVA 好像比较多的解决方案,如果有人有接触过这种动态表达式的开发,不胜感激。
tlday
2020-12-18 20:13:49 +08:00
@xzour 如果我是你,就实时核销,他们要反清帐,对应核销记录就置 revoked 。怎么实时核销,想写了就每次创建收款记录 /发票记录时自己算,不想写了让财务人员自己选,自由还给他们,做错了是他们的问题,你的责任是保证系统不出错,逻辑没问题,尽量提示他们的低级疏忽,尽量简化他们的操作,但没有义务承担他们所有操作的责任。

如果要看客户期末余额,就收款 /发票两个表 group by user_id,sum 一下相减。
如果要看发票剩余金额,就发票 /核销两个表 filter by revoked, group by fapiao_id,sum 一下 filter 出值不为 0 的。



如果不想大改系统,就用 whosesmile 的办法。你要做的实际上是创建这样一个结构:
[
{ 用户 id: xxx, 收款记录: [],发票记录: [] },
{ 用户 id: xxx, 收款记录: [],发票记录: [] }....
]
然后遍历整个数组,每个项里面的两个数组采用类似“合并两个有序数组”的方法对对碰。
xzour
2020-12-18 20:34:00 +08:00
@tlday 大概理解了 3N 遍历与 N^3 遍历的代码区别了,十分感谢!我应该是为了省事,没有重新组装数据结构而导致的,手动核销他们是不愿意的,因为核销逻辑大体思路上就是先进先销,都是重复工作,需要解放劳动力的,未来财务更多是检查,现在这种核销,这只是一种规则,未来有可能只是一种配置,据我了解,这也是一种财务自动化的一个具体实施点,类似 SAP 的自动核销,只是 SAP 目前的核销没有细节到发票构成,不满足我司的使用习惯。


楼上一些回复转字典,HashTable 的我倒现在还不太懂。很抱歉会产生 X-Y 的问题。
tlday
2020-12-18 20:48:05 +08:00
@xzour 他们的意思是类似这种结构
收款记录:HashMap {
xxx: [],
yyy: []...
}
发票记录:HashMap {
xxx: [].
yyy: []...
}
然后遍历 HashMap 两边同 key (用户 id )的数组进行对对碰。


也可以
HashMap{
xxx: Map{ 收款记录: [], 发票记录: [] ... }
yyy: Map{ 收款记录: [], 发票记录: [] ... } ...
}



上面三种结构效果几乎都是等同的,时间复杂度的数量级上不会有什么差异.
xzour
2020-12-18 20:59:11 +08:00
@tlday 这个我就理解清晰多了,我以为是什么 HashMap<key,value> 能把 value 的这个<T>实体的某些字段做成类似索引的效果,达成 O(1),举的例子又是 int,这就超出我对它的认知了。原来是唯一 key 做文章。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/736445

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX