数据库 这样递减的 sql 怎么写

tomnia · 2018年12月30日 · 最后由 darkbaby123 回复于 2019年01月05日 · 7614 次阅读

表 A 有数据:

id n
1 100
2 150
3 120

表 B 有数据:

id m
1 80
2 200

最后的查询结果是:也就是说表 A 和 B 分别以 id 为升序,表 B 的 m 从表 A 的 n 中消耗,最后查询的结果是这个消耗的过程。

id d
1 80
1 20
2 150
3 30

表 B 的 m 从表 A 的 n 中消耗

不懂什么意思,能换个方式阐述吗?

luikore 回复

m 被 n 分解,d 就是被分解的序列,所以 d 的和等于 m 的和 不知道这样表达清楚一些没

是说 A 的 id 是主键,B 的 id 是 A 的外键。然后 n 是初始值,m 是每次 action 的消耗。d 返回经过每次消耗后的剩余的集合?

dsh0416 回复

A 和 B 的 id 都不是主键或外键,只是用以表示 A 和 B 消耗的顺序,用 id 让人误解了。你后面的理解是对的

tomnia 回复

还是没懂,你要不要列一下计算过程

dsh0416 回复

m 80(id=1) 从 n 里的 100(id=1)消耗 80(生成 d 的第一条记录),m 80 消耗完。m 200(id=2) 从 n 的 20(id=1,100 消耗了 80 的剩余,生成 d 的第二条记录),m 剩下 180 从 n 150(id=2) 消耗 150(生成 d 的第三条记录),m 剩下 30 从 n 120 里消耗 30(生成 d 的第四条记录)m 200 消耗完。

tomnia 回复

这样的查询适合单独存一张表吧,否则的话,每次做一定是扫全表,时间复杂度是 O(n+m),放在数据库里做好像没有什么意义啊。

dsh0416 回复

我也有想过把这个计算结果再放一张表里。但表 B 的数据插入顺序不一定与计算的顺序(表 B 的 id)一致,这就导致了有时候表 B 一条记录的插入,这个计算结果表里存在大量的更新,最坏的情况也许是整张表数据的重建,这怎么玩?

tomnia 回复

这个基本上和财务数据的逻辑是一致的,是一个类似单向 (qu) 链 (kuai) 表 (lian) 的结构。中间有一个节点变化,至少会引起之后所有的数据都必须要重算。最坏的情况是变更第一个值,就不得不触发全表重建。

把 A B 表全查出来用代码来处理呗

escyezi 回复

你是正经的吗?😂

tomnia 回复

这样肯定比你从数据库各种 Join 得到的结果快的多,毕竟 O(m+n). 要不只能写过程了,像写 ruby 代码一样在 sql 里遍历一遍。。。

weito 回复

很少手写的代码能比 join 快的

翻完评论才看明白什么意思。我觉得这个计算逻辑不适合用 SQL 做,因为它的关注点并非在集合,而在集合中的每条元素。用两个链表做计算(递归比较头节点)反而更容易,计算结果再保存进数据库就行。

如果有非常特殊的原因一定要在 SQL 里实现,可以看一下 lateral join 和 with recursive。

tomnia 回复

这种需求强行要用 SQL 查询解决的你,是正经的吗?🙃

darkbaby123 回复

还是 33 是个正经人

计算用程序,别用数据库。

darkbaby123 回复

其实这个问题看了评论后第一直觉就是用一个计算结果表去解决,但是用计算结果表可能让解决这个问题变得烦琐起来,因为计算结果表的数据与表 B 里数据的顺序有关,表 B 数据的计算顺序又不一定与产生顺序一致。这就导致要维护计算结果表数据的正确性,需要做很多事情,我目前想到的有:表 B 插入一条数据后,要向计算表里插入若干条结果数据(也可能同时需要删除更多旧的不正确的数据);也许这个计算过程还没结束,表 B 又插入了一条数据呢?...诸如此类的。 所以我想这可能是题主选择用 sql 解决这个问题的原因,这样可能比较省事(如果这样的 sql 写得出来)

@wootaw 是的,如果按照 LZ 的计算过程描述,其实是有不少中间数据的,比较过程化,这并声明式的 SQL 适合表达的领域。而且如果不考虑存储因素的话化,这个需求本质上就是一个链表计算。因此把计算过程和存储分开思考,解决方案的适用性会广很多。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号