带有注释的Django查询集,为啥将GROUP BY应用于所有字段?

Posted

技术标签:

【中文标题】带有注释的Django查询集,为啥将GROUP BY应用于所有字段?【英文标题】:Django queryset with annotate, why is GROUP BY applied to all fields?带有注释的Django查询集,为什么将GROUP BY应用于所有字段? 【发布时间】:2016-03-20 23:32:56 【问题描述】:

我正在使用 Django 1.6 和 PostgreSQL 并具有以下模型:

# models.py
class Game(AbstractContentModel, AbstractScoreModel):
    name = models.CharField(_("name"), max_length=100, blank=True)
    developer = models.CharField(_('Developer'), max_length=255)
    distributor = models.CharField(_('Distributor'), max_length=255, blank=True)
    # ...
    reviews = models.ManyToManyField(Review, related_name="games", blank=True, verbose_name=_("Reviews"))
    videos = models.ManyToManyField(Video, related_name="games", blank=True, verbose_name=_("Videos"))
    images = models.ManyToManyField(Image, related_name="games", blank=True, verbose_name=_("Gallery"))

我正在尝试获取所有游戏,并为每个游戏添加相关视频、评论和图像的数量,如下所示:

# views.py
qs = Game.objects.all()
qs = qs.annotate(video_count=models.Count('videos'))
qs = qs.annotate(review_count=models.Count('reviews'))
qs = qs.annotate(image_count=models.Count('images'))

查询结果为:

SELECT 
"content_game"."id", 
"content_game"."name",
"content_game"."developer", 
"content_game"."distributor",
COUNT("content_game_videos"."video_id") AS "video_count",
COUNT("content_game_reviews"."review_id") AS "review_count", 
COUNT("content_game_images"."image_id") AS "image_count" 
FROM "content_game" 
LEFT OUTER JOIN "content_game_videos" ON ( "content_game"."id" = "content_game_videos"."game_id" )
LEFT OUTER JOIN "content_game_reviews" ON ( "content_game"."id" = "content_game_reviews"."game_id" ) 
LEFT OUTER JOIN "content_game_images" ON ( "content_game"."id" = "content_game_images"."game_id" ) 
GROUP BY 
"content_game"."id", 
"content_game"."name",
"content_game"."developer", 
"content_game"."distributor";

我的问题是 - 为什么 GROUP BY 子句中有所有选定的字段?最重要的是,除了原始 SQL,我该如何摆脱它们?

我收集到使用.values() 会很好,但我想要结果查询中的每个字段。如果我只是使用GROUP BY "content_game"."id",结果是一样的,但是我不知道如何在Django ORM中使用它。

【问题讨论】:

My question is - why there are all selected fields in GROUP BY clause? And most importantly, how can I get rid of them besides raw SQL? Postgresql 不是 mysql more_info 只需尝试在 GROUP BY 中仅使用 "content_game"."id" 执行原始查询 @lad2025 谢谢,问题出在 MySQL 与 PostgreSQL 之间。但如果我真的不需要,我不想使用原始 SQL。我发现此错误已由 code.djangoproject.com/ticket/19259 修复,但我没有看到解决方案。顺便说一句,Django 版本是 1.6,我会将其添加到我的问题中。 最后我通过不计算查询中的相关项目来解决它。我已经在 Game 模型上创建了字段 images_count 和 videos_count,并且我在保存和删除时更新了这些字段。至少这样它总是最适合观看:) 这可能会有所帮助:docs.djangoproject.com/en/1.11/topics/db/aggregation/… 【参考方案1】:

我来不及回答,但我已经研究过答案。首先,OP所说的行为是正确的。它应该适用于所有类型的关系数据库,但正如@lad2025 在评论中所说,唯一的 MySQL(以及 MariaDB)正在为这种行为开脱。因此,我们必须学习它,而不是摆脱它。因为这是使用它/理解它并习惯它的唯一正确方法。

目前 OP 已找到解决方案,即在某处使用行数更新表字段并稍后填充而不是 通过查询即时检索数据。

在使用聚合函数时,您添加到 GROUP BY 中的任何内容都必须包含在 SELECT 中,反之亦然。

"content_game"."id", "content_game"."name", "content_game"."developer",  "content_game"."distributor"

以上字段必须进入 GROUP BY,因为如果我尝试从选择中打印 id 或任何字段,这应该返回哪个行的字段(单个)值?组中的最后一行或第一行不正确(MySQL 会这样做)。所以最好将所有内容都保留在 GROUP BY 中。

为什么?由于不明确 - group by 为每组记录生成一条记录。 Group By 列包含组中所有记录的相同值,并且任何聚合函数都会为组生成单个值,但 "content_game"."developer" 可以与 "content_game"."distributor" 组不同 "content_game"."id" 对于所有行都不同,因为它是主键。所有字段都一样。

很好的解释可以找到here

我希望人们以更好的方式分享(编辑我的帖子)更多声明。所以其他人将来会发现这很有用。

【讨论】:

以上是关于带有注释的Django查询集,为啥将GROUP BY应用于所有字段?的主要内容,如果未能解决你的问题,请参考以下文章

在 Django 中使用带有 GROUP BY 子句的 COUNT(DISTINCT 字段)

Django中GROUP BY中注释的聚合

注释不同的 Django 查询集不再使用不同的查询集

Django 将带有外键的查询集转换为 JSON

Django 查询模型 - GROUP BY、MIN、MAX

Django 在不知道文件名的情况下显示带注释的查询集的值