【问题标题】:Django annotate + SUM how to get all entriesDjango annotate + SUM 如何获取所有条目
【发布时间】:2020-07-28 15:54:04
【问题描述】:

我的模型

class Machine(models.Model):
    machineName = models.CharField(verbose_name="Machine Name", max_length=20, blank=False, null=False)


class SalesReport(models.Model):
    machine = models.ForeignKey(Machine, on_delete=models.CASCADE, null=False, blank=False)
    deviceDate = models.CharField(max_length=200, null=True, blank=True)
    serverDate = models.DateTimeField(auto_now_add=True)
    totalPrice = models.FloatField()

我有 3 台机器,我想获取过去 7 天每台机器的总销售额。

我的查询是

from django.db.models import Sum, Value as V
from django.db.models.functions import Coalesce

SalesReport.objects.values("serverDate__date", "machine__machineName").annotate(
...                 sales=Coalesce(Sum("totalPrice"),V(0))).filter(
...                                                 serverDate__gte=week_start,
...                                                 serverDate__lte=week_end)

结果如下,

[{'serverDate__date': datetime.date(2020, 7, 22), 'machine__machineName': 'machine__1', 'sales': 15.0},
 {'serverDate__date': datetime.date(2020, 7, 28), 'machine__machineName': 'machine__1', 'sales': 145.0},
 {'serverDate__date': datetime.date(2020, 7, 28), 'machine__machineName': 'machine__2', 'sales': 270.0},
 {'serverDate__date': datetime.date(2020, 7, 28), 'machine__machineName': 'machine__3', 'sales': 255.0}]

我想要得到的是

[{'serverDate__date': datetime.date(2020, 7, 22), 'machine__machineName': 'machine__1', 'sales': 15.0},
{'serverDate__date': datetime.date(2020, 7, 22), 'machine__machineName': 'machine__2', 'sales': 0.0},
{'serverDate__date': datetime.date(2020, 7, 22), 'machine__machineName': 'machine__3', 'sales': 0.0},
{'serverDate__date': datetime.date(2020, 7, 28), 'machine__machineName': 'machine__1', 'sales': 145.0},
{'serverDate__date': datetime.date(2020, 7, 28), 'machine__machineName': 'machine__2', 'sales': 270.0},
{'serverDate__date': datetime.date(2020, 7, 28), 'machine__machineName': 'machine__3', 'sales': 255.0}]

我想用 Coalesce 来做,但我弄错了。

*我使用 mysql 作为数据库。一个数据库特定的查询也很好。

【问题讨论】:

    标签: python sql django django-models django-database-functions


    【解决方案1】:

    由于是更多 SQL 问题,我添加了更具体的答案

    SELECT m.machineName, s.price
    FROM machine m LEFT OUTER JOIN (
    SELECT machine_id id, sum(totalPrice) price
    FROM salesreport
    WHERE serverDate BETWEEN DATE_SUB(curdate(), INTERVAL 1 WEEK) and curdate()
    GROUP BY by machine_id) s on m.id = s.id
    

    如果您希望 serverDate 作为输出,您必须应用聚合函数 (Max, Min),因为它位于您的 SalesReport 表中。

    这取决于 serverDate 代表什么。如果是您购买机器的日期,那么它应该在机器表中,并且可以直接从机器表中选择(并且WHERE BETWEEN子句必须存在子选择并且也适用于机器表)。如果它是 salesDate,那么它必须在 SalesReport 中,并且您必须对其应用聚合函数。即:一周内您可能有 7 个日期...

    SELECT m.machineName, s.MaxserverDate, s.price
    FROM machine m LEFT OUTER JOIN (
    SELECT machine_id id, max(serverDate) MaxserverDate, sum(totalPrice) price
    FROM salesreport
    WHERE serverDate BETWEEN DATE_SUB(curdate(), INTERVAL 1 WEEK) and curdate()
    GROUP BY by machine_id) s on m.id = s.id
    

    【讨论】:

      【解决方案2】:

      问题是您在某些日期没有任何销售。这更像是一个特定于数据库的问题,而不是一个 django ORM 问题。我建议在您的机器表上使用 raw sql 和左外连接 => 取出所有机器并在存在时列出销售。

      machine = Machine.objects.raw('''
          SELECT machine.id, machine.name, sales.sid FROM app_machinelist as machine
          LEFT JOIN (select sales_id as sid
                       from app_sales
                      where profile_id = {0}) sales
          ON sales.sid = machine.id
          ORDER BY machine.name ASC
          '''.format(myuser.id))
      

      此示例有效,但出于安全原因,最好通过字典传递参数

      machine = Machine.objects.raw(mysql, params)
      

      在哪里

      params = {'profile_id': pk, 'startdate': startdate, 'enddate': enddate}
      
      mysql = '''
          SELECT machine.id, machine.name, sales.sid FROM app_machinelist as machine
          LEFT JOIN (select sales_id as sid
                       from app_sales
                      where profile_id = %(profile_id)s) sales
          ON sales.sid = machine.id
          ORDER BY machine.name ASC
          '''
      

      【讨论】:

      • 此外,要获取最后 7 天,您可以在 Today 和 serverDate 之间执行日期差异,并在差异大于 8 时保留记录
      • 我需要的是给定日期每台机器的总销售额。我已经在这里完成了。但是如果一台机器没有销售,也应该显示销售为0。
      猜你喜欢
      • 2021-01-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-31
      • 2011-04-23
      • 1970-01-01
      • 2016-10-20
      • 2021-01-06
      相关资源
      最近更新 更多