简体   繁体   English

通过 ForeignKey 外部引用过滤的汇总子查询注释

[英]Summarized Subquery annotation with a filter by ForeignKey outer reference

I try to code the equivalent Django query from this SQL query, but I'm stuck.我尝试从此 SQL 查询中编写等效的 Django 查询,但我被卡住了。 Any help is welcome.欢迎任何帮助。 I receive a race id and from this race I want to do some statistics: nb_race = the number of races from a Horse before the race given, best_chrono = best time from a Horse before the race given.我收到了一个比赛id ,我想从这场比赛中做一些统计: nb_race = 比赛前一匹马的比赛次数, best_chrono = 比赛前一匹马的最佳时间。

SELECT *, (SELECT count(run.id)
                FROM runner run
                INNER JOIN race
                ON run.race_id = race.id
                WHERE run.horse_id = r.horse_id
                AND race.datetime_start < rc.datetime_start 
                ) AS nb_race, 
          (SELECT min(run.chrono)
                FROM runner run
                INNER JOIN race
                ON run.race_id = race.id
                WHERE run.horse_id = r.horse_id
                AND race.datetime_start < rc.datetime_start 
                ) AS best_time
FROM runner r, race rc
WHERE r.race_id = rc.id
AND rc.id = 7890

Django Models: Django 型号:

class Horse(models.Model):
    id = AutoField(primary_key=True)
    name = models.CharField(max_length=255, blank=True, null=True, default=None)

class Race(models.Model):
    id = AutoField(primary_key=True)
    datetime_start = models.DateTimeField(blank=True, null=True, default=None)
    name = models.CharField(max_length=255, blank=True, null=True, default=None)

class Runner(models.Model):
    id = AutoField(primary_key=True)
    horse = models.ForeignKey(Horse, on_delete=models.PROTECT)
    race = models.ForeignKey(Race, on_delete=models.PROTECT)
    chrono = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True, default=None)

Subquery expression can be used to compile an additional queryset as subquery that depends on the main queryset and execute them together as one SQL. 子查询表达式可用于将附加查询集编译为依赖于主查询集的子查询,并将它们作为一个 SQL 一起执行。

from django.db.models import OuterRef, Subquery, Count, Min, F

# prepare a repeated expression about previous runners, but don't execute it yet
prev_run = (
    Runner.objects
    .filter(
        horse=OuterRef('horse'),
        race__datetime_start__lt=OuterRef('race__datetime_start'))
    .values('horse')
)
queryset = (
    Runner.objects
    .values('id', 'horse_id', 'race_id', 'chrono', 'race__name', 'race__datetime_start')
    .annotate(
        nb_race=Subquery(prev_run.annotate(nb_race=Count('id')).values('nb_race')),
        best_time=Subquery(prev_run.annotate(best_time=Min('chrono')).values('best_time'))
    )
)

Some tricks used here are described in the linked docs:链接文档中描述了此处使用的一些技巧

  • Output fields of the Subquery must be restricted by .values(...) to one field: only the aggregated value子查询的 Output 字段必须由.values(...)限制为一个字段:仅聚合值
  • A subquery must be a queryset (that is evaluated lazy and combined together), not a value (that would be evaluated immediately and fail).子查询必须是一个查询集(被评估为惰性并组合在一起),而不是一个值(将立即评估并失败)。 Therefore .annotate() is used in the subquery (not .aggregate() ).因此.annotate()用于子查询(不是.aggregate() )。 That adds a GROUP BY race.horse_id , but is is not a problem becase there is also WHERE race.horse_id =... and the "group by" will be finally ignored by an SQL optimizer in a modern database backend.这增加了一个GROUP BY race.horse_id ,但这不是问题,因为还有WHERE race.horse_id =...并且“group by”最终将被现代数据库后端中的 SQL 优化器忽略。

It is compiled to a query equivalent to the SQL in the example.它被编译为与示例中的 SQL 等效的查询。 Check the SQL:检查 SQL:

>>> print(str(queryset.query))
SELECT ...,
  (SELECT COUNT(U0.id)
   FROM runner U0 INNER JOIN race U1 ON (U0.race_id = U1.id)
   WHERE (U0.horse_id = runner.horse_id AND U1.datetime_start < race.datetime_start)
   GROUP BY U0.horse_id
   ) AS nb_race,
   ...
FROM runner INNER JOIN race ON (runner.race_id = race.id)

A marginal difference is that a subquery uses some internal aliases like U0 and U1.一个微小的区别是子查询使用一些内部别名,如 U0 和 U1。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM