簡體   English   中英

(model → FK → model) 關系上的 Django 注釋

[英]Django annotation on (model → FK → model) relation

宇宙中的星系擁有數百萬/數十億顆恆星,每顆恆星都屬於特定類型,具體取決於其物理特性(紅星、藍超巨星、白矮星等)。 對於我的數據庫中的每顆恆星,我試圖找到同樣類型的恆星所在的不同星系的數量。

class Galaxy(Model):
    ...

class Star(Model):
     galaxy = ForeignKey(Galaxy, related_name='stars')
     type = CharField(...)

可以通過以下方式輕松地為每個 Star 單獨執行此查詢:

star = <some_Star>

desired_galaxies = Galaxy.objects.filter(stars__type=star.type).distinct()
desired_count = desired_galaxies.count()

甚至,雖然更加多余:

desired_count = Star.objects.filter(galaxy__stars__type=star.type).values('galaxy').distinct()

當我嘗試在“單個”查詢中獲取所有星星的計數結果時,這會變得有點模糊:

all_stars = Star.objects.annotate(desired_count=...)

我想這樣做的主要原因是能夠以干凈的方式對Star.objects.order_by('desired_count')進行排序。

到目前為止我嘗試過的:

Star.annotate(desired_count=Count('galaxy', filter=Q(galaxy__stars__type=F('type')), distinct=True))

但這為每顆星注釋了1 我想我將不得不在這里使用OuterRef, Subquery ,但不確定如何使用。

您可以使用GROUP BY來獲取計數:

Star.objects.values('type').annotate(desired_count=Count('galaxy')).values('type', 'desired_count')

Django 沒有提供一種方法來定義不涉及外鍵的模型之間的多值關系。 如果是這樣,你可以做類似的事情

class Galaxy(Model):
    ...

class Star(Model):
    galaxy = ForeignKey(Galaxy, related_name='stars')
    type = CharField(...)
    same_type_stars = Relation(
        'self', from_fields=('type',), to_fields=('type',)
    )

Star.objects.annotate(
    galaxies_count=Count('same_type_stars__galaxy', distinct=True)
)

這會導致一些事情

SELECT
  star.*,
  COUNT(DISTINCT same_star_type.galaxy_id) galaxies_count
FROM star
LEFT JOIN star same_star_type ON (same_star_type.type = star.type)
GROUP BY star.id

如果你想實現類似的東西,你現在需要使用子查詢

Star.objects.annotate(
    galaxies_count=Subquery(Star.objects.filter(
        type=OuterRef('type'),
    ).values('type').values(
        inner_count=Count('galaxy', distinct=True),
    ))
)

這會導致一些事情

SELECT
   star.*,
   (
       SELECT COUNT(DISTINCT inner_star.galaxy_id)
       FROM star inner_star
       WHERE inner_star.type = star.type
       GROUP BY inner_star.type
   ) galaxies_count
FROM star

這可能在一些沒有實現相關子查詢的數據庫(例如 MySQL)上表現不佳。 在所有情況下,請確保對Star.type進行索引,否則Star.type都會獲得糟糕的性能。 ('type', 'galaxy')上的復合索引可能會更好,因為它可能允許您執行僅索引掃描(例如在 PostgreSQL 上)。

暫無
暫無

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

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