【问题标题】:Auto calculated column based on current & previous row data, that will be used for upcoming rows SQL, Django基于当前和前一行数据的自动计算列,将用于即将到来的行 SQL、Django
【发布时间】:2020-10-25 04:56:15
【问题描述】:

背景:一个 ERP 工具,将保存所有交易信息,如用户的期初余额和交易金额以及期末余额。当前行的期初余额取决于前一行的期末余额。期末余额是根据期初余额和交易金额的加减计算得出的。此计算的期末余额将用作下一行的期初余额,以此类推。

注意:交易金额等交易信息来自库存中的产品购买

问题:假设我有 100 个条目。管理员出于某种原因想要更改第一个条目的交易金额。因此,我第一行的期末余额将发生变化。但问题是所有其他 99 行都取决于第一行的期末余额。如何创建解决此依赖列数据问题的 SQL 表。

PS:我使用的是Django作为框架,但是Raw SQL查询&解释也会在一定程度上解决我的问题。

【问题讨论】:

    标签: python mysql django erp


    【解决方案1】:

    你真的应该发布你的模型......

    但是给定一个像这样的基本结构:

    from django.db import models, transaction
    from django.utils import timezone
    
    
    class Account(models.Model):
        iban = models.CharField(max_length=34, primary_key=True)
    
        @property
        def balance(self):
            return self.mutations.last().end
    
    
    class AccountMutation(models.Model):
        id = models.BigAutoField(primary_key=True)
        account = models.ForeignKey(
            Account, on_delete=models.PROTECT, related_name="mutations"
        )
        start = models.DecimalField(decimal_places=4, max_digits=12)
        timestamp = models.DateTimeField(default=timezone.now)
        amount = models.DecimalField(decimal_places=4, max_digits=12)
        end = models.DecimalField(decimal_places=4, max_digits=12)
        objects = AccountMutationManager()
    
        class Meta:
            ordering = ("timestamp", "pk")
    

    我们可以这样实现自定义管理器:

    class AccountMutationManager(models.Manager):
        @transaction.atomic
        def recalculate(self, from_: "AccountMutation"):
            qs = self.filter(
                account=from_.account, timestamp__gte=from_.timestamp, id__gt=from_.id
            ).select_for_update()
            prev = from_
            for mutation in qs:
                mutation.start = prev.end
                mutation.end = mutation.start + mutation.amount
                mutation.save()
                prev = mutation
    

    当然,这是基于基于时间戳的突变来确定排序的。如果您使用前一个指针链接事务,则选择会略有不同,您需要一个 reorder() 方法以防时间戳发生更改。但这实际上取决于它的存储方式。

    通常情况下,我不会使用突变存储开始和结束余额,而是将它们设为计算字段,并且每个帐户都会有每个“会计年度”的期初余额,从该开始计算。

    一些说明:

    • 我们对原子事务使用全有或全无的方法
    • 此外,我们使用select_for_update() 锁定所有将受到影响的行,这样就不会同时发生影响相同数据的两次重新计算。
    • from_ 是已更改并假定具有正确最终平衡的突变

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-11-12
      • 2018-12-30
      • 2019-01-15
      • 1970-01-01
      • 1970-01-01
      • 2021-10-27
      • 2021-10-29
      • 1970-01-01
      相关资源
      最近更新 更多