我有一个返回大量数据的查询集,可以按年份过滤该查询集,该查询集将返回约10万行,或者显示所有将带来约100万行的数据。

该注释的目的是生成一个xlsx电子表格。

模型表示形式,RelatedModelModelAnotherModel之间是很多的

Model:
    id
    field1
    field2
    field3

RelatedModel:
    foreign_key_model (Model)
    foreign_key_another (AnotherModel)

Queryset,如果该关系存在,它将进行注释,此注释非常慢,可能需要几分钟。
Model.objects.all().annotate(
    related_exists=Exists(RelatedModel.objects.filter(foreign_key_model=OuterRef('id'))),
    related_column=Case(
        When(related_exists=True, then=Value('The relation exists!')),
        When(related_exists=False, then=Value('The relation doesn't exist!')),
        default=Value('This is the default value!'),
        output_field=CharField(),
    )
).values_list(
    'related_column',
    'field1',
    'field2',
    'field3'
)

最佳答案

如果仅需要更改xlsx中True/False的显示方式-一种选择是仅具有一个related_exists BooleanField批注,然后自定义在创建xlsx文档时如何转换它-即在序列化器中。数据库应存储原始/未格式化的值,然后应用程序准备将其显示给用户。

要考虑的其他事项:

  • 索引以加快过滤速度。
  • 如果过滤后有数百万条记录,则在一个表中-也许可以考虑对表进行分区。


  • 但是,让我们看一下原始查询的原始sql。 它将是这样的:

    SELECT [model_fields],
           EXISTS([CLIENT_SELECT]) AS related_exists,
           CASE
           WHEN EXISTS([CLIENT_SELECT]) = true THEN 'The relation exists!'
           WHEN EXISTS([CLIENT_SELECT]) = true THEN 'The relation does not exist!'
           ELSE 'The relation exists!'
           END AS related_column
    FROM model;
    

    马上,我们可以看到存在Exists的嵌套查询CLIENT_SELECT 是的3倍。即使完全相同,也可以执行最少2次,最多3次。数据库可能会将其优化为快于3倍,但仍不是1倍的最佳。

    首先,EXISTS返回True或False,我们只需要检查一下它是否为True,就可以将'The relation does not exist!'设置为默认值。
        related_column=Case(
            When(related_exists=True, then=Value('The relation exists!')),
            default=Value('The relation does not exist!')
    

    为什么related_column再次执行相同的选择而不选择related_exists的值?

    因为我们在计算其他列时不能引用计算所得的列-这是django知道的数据库级约束,并且重复了表达式。

    等待,那么我们实际上不需要related_exists列,只需要在CASE语句中保留related_column并保留1个子查询即可。

    Django来了-在第一个注释之前,我们(直到3.0版)不能在过滤器中使用表达式。

    因此,我们的情况就像:为了在Exist中使用When,我们首先需要将其添加为注释,但它不会用作引用,而是表达式的完整副本。

    好消息!

    由于Django 3.0,我们可以使用直接在QuerySet过滤器中输出BooleanField的表达式,而不必首先注释Exists是此类BooleaField表达式之一。
    Model.objects.all().annotate(
        related_column=Case(
            When(
                Exists(RelatedModel.objects.filter(foreign_key_model=OuterRef('id'))),
                then=Value('The relation exists!'),
            ),
            default=Value('The relation doesn't exist!'),
            output_field=CharField(),
        )
    )
    
    

    并且只有一个嵌套的select和一个带注释的字段。

    Django 2.1、2.2

    这是commit,它确定了 bool 表达式的允许量,尽管之前已为其添加了许多前提条件。其中之一是表达式对象上是否存在conditional属性,并检查该属性。

    因此,尽管不推荐未测试,但对于Django 2.1、2.2来说似乎很少起作用(在没有conditional检查之前,它将需要进行更多侵入式更改):
  • 创建Exists表达式实例
  • 猴子用conditional = True修补它
  • 将其用作When语句
  • 中的条件
    related_model_exists = Exists(RelatedModel.objects.filter(foreign_key_model=OuterRef('id')))
    
    setattr(related_model_exists, 'conditional', True)
    
    Model.objects.all().annotate(
        related_column=Case(
            When(
                relate_model_exists,
                then=Value('The relation exists!'),
            ),
            default=Value('The relation doesn't exist!'),
            output_field=CharField(),
        )
    )
    
    

    相关检查

    relatedmodel_set__isnull=True 检查是,而不是,其原因如下:
  • 它执行LEFT OUTER JOIN-比EXISTS效率低
  • 它执行LEFT OUTER JOIN-它连接表,这使其仅适用于filter()条件(不适用于注释-时间),并且仅适用于OneToOne或OneToMany(一个在relatedmodel一侧)关系
  • 关于django - 使用现有注释时提高Django查询集性能,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/59079165/

    10-16 08:27