簡體   English   中英

通過 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)

子查詢表達式可用於將附加查詢集編譯為依賴於主查詢集的子查詢,並將它們作為一個 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'))
    )
)

鏈接文檔中描述了此處使用的一些技巧

  • 子查詢的 Output 字段必須由.values(...)限制為一個字段:僅聚合值
  • 子查詢必須是一個查詢集(被評估為惰性並組合在一起),而不是一個值(將立即評估並失敗)。 因此.annotate()用於子查詢(不是.aggregate() )。 這增加了一個GROUP BY race.horse_id ,但這不是問題,因為還有WHERE race.horse_id =...並且“group by”最終將被現代數據庫后端中的 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。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM