【问题标题】:ProgrammingError when aggregating over an annotated & grouped Django ORM query聚合带注释和分组的 Django ORM 查询时出现编程错误
【发布时间】:2010-04-01 08:11:47
【问题描述】:

我正在尝试构建一个查询来获取“每个用户购买的商品的平均、最大和最小数量”。

数据源就是这个简单的销售记录表:

class SalesRecord(models.Model):
    id           = models.IntegerField(primary_key=True)
    user_id      = models.IntegerField()
    product_code = models.CharField()
    price        = models.IntegerField()
    created_at   = models.DateTimeField()

用户购买的每件商品都会在此表中插入一条新记录。 [注意]:user_id 不是同一数据库中表的外键,因为此后端系统不管理用户信息。该值由产品的前端部分提供。

这是我构建查询的尝试:

q = SalesRecord.objects.all()
q = q.values('user_id').annotate(   # group by user and count the # of records
    count=Count('id'),              # (= # of items)
    ).order_by()
result = q.aggregate(Max('count'), Min('count'), Avg('count'))

当我尝试执行代码时,在最后一行出现ProgrammingError:

(1064, "您的 SQL 中有错误 句法;检查手册 对应你的 MySQL 服务器 使用正确语法的版本 靠近'从(选择 sales_records.user_idAS user_id, COUNT(sales_records.`' 在第 1 行")

Django 的错误画面显示 SQL 是

SELECT FROM
  (SELECT
    `sales_records`.`user_id` AS `user_id`,
    COUNT(`sales_records`.`id`) AS `count`
  FROM `sales_records`
  WHERE (`sales_records`.`created_at` >= %s AND `sales_records`.`created_at` <= %s )
  GROUP BY `sales_records`.`user_id` ORDER BY NULL) subquery

它没有选择任何东西!有人可以告诉我正确的方法吗?

破解 Django

我发现清除django.db.models.sql.query.BaseQuery.get_aggregation() 中选定字段的缓存似乎可以解决问题。虽然我不确定这是修复还是解决方法。

@@ -327,10 +327,13 @@
    # Remove any aggregates marked for reduction from the subquery
    # and move them to the outer AggregateQuery.
+   self._aggregate_select_cache = None
+   self.aggregate_select_mask = None
    for alias, aggregate in self.aggregate_select.items():
        if aggregate.is_summary:
            query.aggregate_select[alias] = aggregate
-           del obj.aggregate_select[alias]
+           if alias in obj.aggregate_select:
+               del obj.aggregate_select[alias]

...产生结果:

{'count__max': 267, 'count__avg': 26.2563, 'count__min': 1}

【问题讨论】:

    标签: django django-models django-aggregation


    【解决方案1】:

    按原样使用模型(对用户没有 FK),您可以获得 user_id 计数,然后自己进行数学运算:

    counts = SalesRecord.objects.values('user_id').\
            annotate(count=Count('id')).values_list('count', flat=True)
    (max(counts), min(counts), sum(counts) / float(len(counts)))
    

    如果您能够更改表格以使用 ForeignKey 并使您的模型看起来更像这样:

    class SalesRecord(model.Models):
        user = model.ForeignKey(User)
        product_code = models.CharField()
        price        = models.IntegerField()
        created_at   = models.DateTimeField()
    

    然后您可以从 User 对象解决问题并使用聚合():

    users_with_counts = Users.objects.annotate(count=Count('salesrecord'))
    stats = users_with_counts.aggregate(Max('count'), Min('count'), Avg('count'))
    

    任何一种方式都可以通过单个数据库查询为您提供所需的内容。

    【讨论】:

    • 感谢您的建议。不幸的是,它不是model.ForeignKey。当我剥离模型和查询以简化问题时,名称差异悄悄出现。 (问题现已修改)
    • 根据修改后的问题更新。您似乎对 in aggregate() 感到好奇,所以即使您现在无法修改模型,我也留在了修改后的模型解决方案中。
    • 看起来你得到了正确的代码,但是如果你在 Django 1.6+ 上运行它,你会得到ProgrammingError: syntax error at or near "FROM",就像这样:stackoverflow.com/questions/23279393/… 我不敢相信它还没有修复.
    【解决方案2】:

    您的 ORM 查询确实是正确的,但错误出现在 Django 1.6 中。显然它已在 1.7 中修复。来源:https://code.djangoproject.com/ticket/23669#comment:5

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-01-26
      • 2021-01-20
      • 2019-08-03
      • 2020-05-23
      • 2023-03-29
      • 2018-08-19
      • 2017-07-21
      • 1970-01-01
      相关资源
      最近更新 更多