通过 ForeignKey 外部引用筛选的汇总子查询注释
Summarized Subquery annotation with a filter by ForeignKey outer reference
我尝试根据这个 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)
Subquery expression 可用于将附加查询集编译为依赖于主查询集的子查询,并将它们作为一个 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(...)
限制为一个字段:只有聚合值
- 子查询必须是查询集(惰性计算并组合在一起),而不是值(将立即计算并失败)。因此在子查询中使用
.annotate()
(而不是 .aggregate()
)。这增加了一个 GROUP BY race.horse_id
,但这不是问题,因为还有 WHERE race.horse_id = ...
并且“分组依据”最终将被现代数据库后端中的 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。
我尝试根据这个 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)
Subquery expression 可用于将附加查询集编译为依赖于主查询集的子查询,并将它们作为一个 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(...)
限制为一个字段:只有聚合值 - 子查询必须是查询集(惰性计算并组合在一起),而不是值(将立即计算并失败)。因此在子查询中使用
.annotate()
(而不是.aggregate()
)。这增加了一个GROUP BY race.horse_id
,但这不是问题,因为还有WHERE race.horse_id = ...
并且“分组依据”最终将被现代数据库后端中的 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。