【问题标题】:Group by limit 1 in django在 Django 中按限制 1 分组
【发布时间】:2016-11-01 21:59:11
【问题描述】:

我在 Django 中有以下模型(为简洁起见):

class DistinctWord(models.Model):
    ...

class Word(models.Model):
    distinct_word = models.ForeignKey('DistinctWord', related_name='words')
    ...

class UserWord(models.Model):
    distinct_word = models.ForeignKey(DistinctWord, related_name='user_words')
    ...

在单词中:DistinctWord 是相互派生的单词的根(例如,silly、sillier、silliest),而 UserWord 是用户的字典。因此,当用户将一个单词添加到他的字典中时,他实际上添加了根单词(以及所有相关的单词)。所以,当他要求查看/研究他字典中的单词时,我必须带一个实际的单词(例如第一个单词)。

也就是说,对于给定的 UserWords 查询集(例如 uw),我想检索与该查询集中每一行相关的第一个 Word(最好是一次或几次访问数据库,而不是每次访问一次)排)。这将是原始 sql 中的简单连接、分组和限制 1,但我无法在 Django 中完全理解它。

【问题讨论】:

标签: django django-models


【解决方案1】:

怎么样(给定您的 uw 查询集):[obj.words.first() for obj in uw]

【讨论】:

  • 这将对数据库产生大量查询,这可能是 OP 试图避免的。
  • 是的。我的意思是一次访问数据库。
【解决方案2】:

让:

uw # be a given queryset of UserWord's
dw # be a queryset of DistinctWords (will be derived from `uw`)
w  # be a queryset of Words needed (will be derived from `dw`)

每个UserWord 有一个DistinctWord,每个DistinctWord 有很多Word(大致记为uw>dw<w)。

这是我的答案:

dw_id=uw.values_list('distinct_word_id', flat=True) # 1: get dw ids from uw
dw=DistinctWord.objects.filter(id__in=dw_id)        # 2: get dw's
w_first_id=dw.annotate(first_word=Min('words')).values_list('first_word', flat=True) 
                                                    # 3: find id of first word 
w=Word.objects.filter(id__in=w_first_id)            # 4: get first words

总而言之:第 1 行和第 2 行得到 dw,应该只是 1 次访问数据库

第 3 行使用annotate 后跟values_list 来查找第一个相关Word 的id

第 4 行带来了上一步中生成的 id 中的实际 Word 对象。第 3 行和第 4 行应该是对数据库的另一次访问,因为 annotate 不是终端语句。

因此 2 次访问数据库(未测试)。

【讨论】:

    【解决方案3】:

    您可以使用子查询 API 做到这一点:

    from django.db.models.expressions import Subquery, OuterRef
    
    first_word = Word.objects.filter(
        distinct_word=OuterRef('distinct_word')
    ).order_by('pk').values('pk')[:1]
    
    UserWord.objects.filter(
         # whatever filters...
    ).annotate(
         first_word=Subquery(first_word)
    )
    

    这将导致 SQL 看起来 类似于

    SELECT user_word.*,
           (SELECT word.id 
              FROM word 
             WHERE word.distinct_word_id = user_word.distinct_word_id
           ) AS first_word
      FROM user_word
     WHERE ...
    

    这可能不如在 postgres 中使用 DISTINCT ON 的 JOIN 执行得好,并且可能不如使用 GROUP BY 的 JOIN 执行得好,因为它需要为每一行执行子查询。

    【讨论】:

      猜你喜欢
      • 2011-02-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-11
      • 2020-02-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多