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

如何用Django ORM实现左连接、分组及销售额求和?附原生SQL疑问

问题解答

一、用Django ORM实现对应SQL的查询结果

你可以通过Django的聚合函数和数据库函数实现和目标SQL一致的效果,无需编写原生SQL:

from django.db.models import Sum
from django.db.models.functions import ExtractDay, ExtractMonth

# 生成销售统计结果
sales_summary = Sales.objects.values(
    # 关联产品名称作为分组字段
    'product_id__name',
    # 提取日期的日、月,作为分组字段
    day=ExtractDay('date'),
    month=ExtractMonth('date')
).annotate(
    # 计算单组销售额总和
    total_sales=Sum('price')
).order_by('month', 'day', 'product_id__name')

代码说明:

  • values() 指定分组依据的字段,包括关联的产品名称、日期的日和月;
  • ExtractDay/ExtractMonth 是Django内置数据库函数,对应SQL中的extract(day from date)和extract(month from date);
  • annotate(Sum('price')) 实现销售额求和逻辑;
  • order_by() 保证结果按月份、日期、产品名称排序,与原SQL分组顺序一致。

遍历结果时,每个元素是字典,格式示例:

{'product_id__name': '产品A', 'day': 15, 'month': 10, 'total_sales': 1500.0}

二、Sales.objects.raw(sql)报错原因

Django的Model.objects.raw()方法设计目的是将原生SQL查询结果映射为对应模型的实例,因此它要求查询结果必须包含该模型的主键字段(即Sales表的id字段)。你的SQL查询未选择s.id,因此触发了"Raw query must include the primary key"错误。

如果不需要将结果映射为Sales实例,有两种解决方式:

  1. 在原生SQL中添加主键字段:
select s.id, p.name, extract(day from date) as day, extract(month from date) as month, sum(s.price)
from timetracker.main_sales s
left join timetracker.main_product p on p.id = s.product_id_id
group by month, day, p.name, s.id;
  1. 直接使用Django数据库游标执行查询,获取原始结果:
from django.db import connection

with connection.cursor() as cursor:
    cursor.execute("""
        select p.name, extract(day from date) as day, extract(month from date) as month, sum(s.price)
        from timetracker.main_sales s
        left join timetracker.main_product p on p.id = s.product_id_id
        group by month, day, p.name;
    """)
    results = cursor.fetchall()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 20:05:42