【发布时间】:2020-05-08 11:05:37
【问题描述】:
尽管我一直在尝试基于我的网络搜索,但我仍然很难让查询正常工作,而且我认为在发疯之前我需要一些帮助。
我有四个模型:
class Series(models.Model):
puzzles = models.ManyToManyField(Puzzle, through='SeriesElement', related_name='series')
...
class Puzzle(models.Model):
puzzles = models.ManyToManyField(Puzzle, through='SeriesElement', related_name='series')
...
class SeriesElement(models.Model):
puzzle = models.ForeignKey(Puzzle,on_delete=models.CASCADE,verbose_name='Puzzle',)
series = models.ForeignKey(Series,on_delete=models.CASCADE,verbose_name='Series',)
puzzle_index = models.PositiveIntegerField(verbose_name='Order',default=0,editable=True,)
class Play(models.Model):
puzzle = models.ForeignKey(Puzzle, on_delete=models.CASCADE, related_name='plays')
user = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True,null=True, on_delete=models.SET_NULL, related_name='plays')
series = models.ForeignKey(Series, blank=True, null=True, on_delete=models.SET_NULL, related_name='plays')
puzzle_completed = models.BooleanField(default=None, blank=False, null=False)
...
每个用户可以多次玩任何谜题,每次都会创建一个Play 记录。
这意味着对于一组给定的(user,series,puzzle),我们可以有多个Play 记录,
有些是puzzle_completed = True,有些是puzzle_completed = False
我正在尝试(未成功)实现的是,通过注释计算每个系列的谜题数量 nb_completed_by_user 和 nb_not_completed_by_user。
对于nb_completed_by_user,我有一些几乎适用于所有情况的东西(我的一个测试中有一个故障,到目前为止我无法解释):
Series.objects.annotate(nb_completed_by_user=Count('puzzles',
filter=Q(puzzles__plays__puzzle_completed=True,
puzzles__plays__series_id=F('id'),puzzles__plays__user=user), distinct=True))
对于nb_not_completed_by_user,我能够在Puzzle 上进行查询,这给了我很好的答案,但我无法将其转换为Subquery 表达式,该表达式可以正常工作而不会引发错误,或者得到一个Count 表达式给我正确的答案。
这个有效:
puzzles = Puzzle.objects.filter(~Q(plays__puzzle_completed=True,
plays__series_id=1, plays__user=user),series=s)
但是当尝试移动到子查询时,我找不到使用以下表达式不抛出错误的方法:ValueError: This queryset contains a reference to an outer query and may only be used in a subquery.
pzl_completed_by_user = Puzzle.objects.filter(plays__series_id=OuterRef('id')).exclude(
plays__puzzle_completed=True,plays__series_id=OuterRef('id'), plays__user=user)
下面的Count 表达式没有给我正确的结果:
Series.objects.annotate(nb_not_completed_by_user=Count('puzzles', filter=~Q(
puzzle__plays__puzzle_completed=True, puzzle__plays__series_id=F('id'),
puzzle__plays__user=user))
谁能解释我如何获得这两个值? 并最终向我提出一个链接,该链接清楚地解释了如何将子查询用于比官方文档中不太明显的情况
提前致谢
2021 年 3 月编辑: 我最近发现了两篇文章,它们指导我解决了这个特定问题的一个潜在解决方案: Django Count and Sum annotations interfere with each other 和 Django 1.11 Annotating a Subquery Aggregate
我从https://stackoverflow.com/users/188/matthew-schinckel 和https://stackoverflow.com/users/1164966/benoit-blanchon 实施了建议的解决方案
有帮助课程:class SubqueryCount(Subquery) 和 class SubquerySum(Subquery)
class SubqueryCount(Subquery):
template = "(SELECT count(*) FROM (%(subquery)s) _count)"
output_field = PositiveIntegerField()
class SubquerySum(Subquery):
template = '(SELECT sum(_sum."%(column)s") FROM (%(subquery)s) _sum)'
def __init__(self, queryset, column, output_field=None, **extra):
if output_field is None:
output_field = queryset.model._meta.get_field(column)
super().__init__(queryset, output_field, column=column, **extra)
效果非常好!并且比传统的 Django Count 注释要快得多。 ... 至少在 SQlite 中,可能是其他人所说的 PostgreSQL。
但是当我在 MariaDB 环境中尝试时……它崩溃了! MariaDB 显然不能/不愿意处理相关子查询,因为这些子查询被认为是次优的。
在我的例子中,当我尝试从数据库中同时为每条记录获取多个 Count/distinct 注释时,我真的看到了性能的巨大提升(在 SQLite 中) 我想在 MariaDB 中复制。
有人能帮我找出一种方法来为 MariaDB 实现这些辅助函数吗?
template 在这个环境中应该是什么?
马修-辛克尔? 贝努瓦布兰琼? rktavi ?
【问题讨论】:
-
@Bhargav Rao :我提供的是一种适用于多种环境的解决方案。我发现您在没有评论或解释的情况下删除它有点粗糙。由于我缺少 9 个学分来发表评论,因此发布部分答案以使事情顺利进行对我来说有点困难。感谢您的理解
-
您可以从答案中编辑出后续问题,使其成为独立的解决方案并标记为取消删除。
-
当你说“它崩溃了”时,你的意思是它崩溃了 MariaDB,还是返回了一个错误?如果是这样,错误是什么?如果我们能确切地看到问题出在哪里,这可能有助于理解问题。
-
@MatthewSchinckel:抱歉滥用语言;它在查询(226._mysql.connection.query(self,查询) - 在 MariaDB 文档中查看它,我发现 MariaDB 不支持“相关子查询”(mariadb.com/kb/en/subquery-limitations)
标签: django django-queryset correlated-subquery