如何用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实例,有两种解决方式:
- 在原生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;
- 直接使用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
相关产品推荐
相关产品推荐

