通过foreignkey外部引用筛选的摘要子查询注解

lvjbypge  于 2021-07-24  发布在  Java
关注(0)|答案(1)|浏览(355)

我试图从这个sql查询中编写等效的django查询,但是我被卡住了。欢迎任何帮助。我接受比赛 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型号:

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)
bybem2ql

bybem2ql1#

子查询表达式可用于将附加查询集编译为依赖于主查询集的子查询,并将它们作为一个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'))
    )
)

链接文档中描述了此处使用的一些技巧:
子查询的输出字段必须受 .values(...) 到一个字段:仅聚合值
一个子查询必须是一个queryset(它是惰性的并组合在一起计算的),而不是一个值(它将被立即计算并失败)。因此 .annotate() 在子查询中使用(不是 .aggregate() ). 这增加了 GROUP BY race.horse_id ,但这不是问题,因为 WHERE race.horse_id = ... 而“groupby”最终将被现代数据库后端中的sql优化器忽略。
它被编译为与示例中的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)

一个细微的区别是子查询使用一些内部别名,如u0和u1。

相关问题