You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Django ORM计算产品可用数量结果不准确问题排查

Django ORM计算产品可用数量结果不准确的问题与修复

问题背景

使用Django ORM为Products查询集注释生产数量、销售数量及可用数量字段时,计算结果严重偏离实际值;但采用原生SQL执行相同逻辑时结果准确。

原ORM代码(结果不准确)

def get_queryset(self):
    queryset = super().get_queryset()
    queryset = queryset.annotate(Quantity_Produced=Sum(F('production__qunatity_produced')))
    queryset = queryset.annotate(Quantity_Sold=Sum(F('sales__qunatity_delivered')))
    queryset = queryset.annotate(Quantity_available=Sum(F('production__qunatity_produced'))
                                                        - Sum(F('sales__qunatity_delivered')))
    return queryset

ORM输出结果

{
    "product_id": 1,
    "product_name": "Product 1",
    "weight": 10.0,
    "Quantity_Produced": 6300.0,
    "Quantity_Sold": 2600.0,
    "Quantity_available": 3700.0
}

原生SQL代码(结果准确)

def get_queryset(self):
    queryset= models.Products.objects.raw('''
        SELECT
            *,
            (SELECT SUM(q.qunatity_produced) FROM production q WHERE q.product_id = p.product_id) AS Quantity_Produced ,
            (SELECT SUM(s.qunatity_delivered) FROM Sales s WHERE s.product_id = p.product_id) AS Quantity_Sold,
            sum((SELECT SUM(q.qunatity_produced) FROM production q WHERE q.product_id = p.product_id)
            -(SELECT SUM(s.qunatity_delivered) FROM Sales s WHERE s.product_id = p.product_id))as Quantity_available
        FROM
            products p
            group by Product_id
            order by Product_id
        ''')
    return queryset

原生SQL输出结果

{
    "product_id": 1,
    "product_name": "Product 1",
    "weight": 10.0,
    "Quantity_Produced": 700.0,
    "Quantity_Sold": 260.0,
    "Quantity_available": 440.0
}

模型定义

class Products(models.Model):
    product_id = models.AutoField(primary_key=True)  
    product_name = models.CharField(max_length=255)
    weight = models.ForeignKey(Bags, models.DO_NOTHING)


class Production(models.Model):
    production_id = models.AutoField(primary_key=True)
    product = models.ForeignKey('Products', models.DO_NOTHING)
    date_of_production = models.DateField()
    qunatity_produced = models.FloatField()
    unit_price = models.FloatField()


class Sales(models.Model):
    sales_id = models.AutoField(primary_key=True)
    date_of_sale = models.DateField()
    customer = models.ForeignKey(Customers, models.DO_NOTHING)
    product = models.ForeignKey(Products, models.DO_NOTHING)
    qunatity_delivered = models.FloatField()
    unit_price = models.FloatField()
    total_amount = models.FloatField(default=0.0)
    payment_type = models.ForeignKey(Paymenttype, models.DO_NOTHING)
    description = models.TextField(blank=True, null=True)
    address= models.CharField(max_length=255)

失效原因

ORM代码执行时,Django会对Products与Production、Sales进行多表左连接,当一个产品存在多条生产记录和多条销售记录时,会产生笛卡尔积(即生产记录数 × 销售记录数)。此时使用Sum聚合会重复计算每条记录:

  • 生产数量会被重复计算「销售记录数」次
  • 销售数量会被重复计算「生产记录数」次
    最终导致聚合结果被放大,与实际值不符。

而原生SQL采用子查询方式,分别针对每个产品单独计算生产、销售的总和,避免了多表连接带来的笛卡尔积问题。

修复方案

使用Django的Subquery和OuterRef实现子查询逻辑,模拟原生SQL的计算方式,避免笛卡尔积:

from django.db.models import Subquery, OuterRef, Sum, F

def get_queryset(self):
    # 定义生产数量子查询:按产品分组计算总生产数量
    production_subquery = Production.objects.filter(
        product=OuterRef('pk')
    ).values('product').annotate(
        total_produced=Sum('qunatity_produced')
    ).values('total_produced')

    # 定义销售数量子查询:按产品分组计算总销售数量
    sales_subquery = Sales.objects.filter(
        product=OuterRef('pk')
    ).values('product').annotate(
        total_sold=Sum('qunatity_delivered')
    ).values('total_sold')

    queryset = super().get_queryset()
    queryset = queryset.annotate(
        Quantity_Produced=Subquery(production_subquery, output_field=models.FloatField()),
        Quantity_Sold=Subquery(sales_subquery, output_field=models.FloatField())
    ).annotate(
        # 基于已注释的字段计算可用数量,避免重复聚合
        Quantity_available=F('Quantity_Produced') - F('Quantity_Sold')
    )
    return queryset

修复后效果

执行修复后的ORM代码,将得到与原生SQL一致的准确结果:

{
    "product_id": 1,
    "product_name": "Product 1",
    "weight": 10.0,
    "Quantity_Produced": 700.0,
    "Quantity_Sold": 260.0,
    "Quantity_available": 440.0
}

内容的提问来源于stack exchange,提问作者Mudabir Naqvi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 05:21:56