Hus*_*ell 6 django django-queryset query-performance
我有一个返回大量数据的查询集,它可以按年份过滤,将返回大约 10 万行,或显示所有将带来大约 100 万行。
此注释的目的是生成一个 xlsx 电子表格。
模型表示,RelatedModel在Model和之间是多对多的AnotherModel
Model:
id
field1
field2
field3
RelatedModel:
foreign_key_model (Model)
foreign_key_another (AnotherModel)
Run Code Online (Sandbox Code Playgroud)
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'
)
Run Code Online (Sandbox Code Playgroud)
如果只需要更改 xlsx 中 True / False 的显示方式 - 一种选择是只有一个related_existsBooleanField 注释,然后在创建 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;
Run Code Online (Sandbox Code Playgroud)
而且马上就可以看到嵌套查询已存在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!')
Run Code Online (Sandbox Code Playgroud)
为什么related_column再次执行相同的选择而不是取值related_exists?
因为我们无法在计算另一列时引用计算列- 这是 django 知道并复制表达式的数据库级约束。
等等,那么我们实际上不需要related_exists列,让我们留下related_columnCASE 语句和 1 个存在子查询。
Django 来了——我们不能(直到 3.0)在过滤器中使用表达式而不先注释它们。
所以,在我们的情况下是这样的:为了使用Existin 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(),
)
)
Run Code Online (Sandbox Code Playgroud)
并且只有一个嵌套选择和一个带注释的字段。
Django 2.1、2.2
这是最终确定允许布尔表达式的提交,尽管之前添加了许多先决条件。其中之一是conditional表达式对象上存在属性并检查此属性。
所以,虽然不建议使用,并没有测试它似乎很有点工作黑客Django的2.1,2.2(以前没有conditional检查,这将需要更深入的变化):
Exists表达式实例conditional = TrueWhen语句中的条件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(),
)
)
Run Code Online (Sandbox Code Playgroud)
相关检查
relatedmodel_set__isnull=True检查不适合有以下几个原因:
LEFT OUTER JOIN- 效率低于EXISTSLEFT OUTER JOIN- 它连接表,这使它仅适用于 filter() 条件(不适用于注释 - When),并且仅适用于 OneToOne 或 OneToMany(一个在相关模型端)关系您可以大大简化查询:
from django.db.models import Count
Model.objects.all().annotate(
related_column=Case(
When(relatedmodel_set__isnull=True, then=Value("The relation doesn't exist!")),
default=Value("The relation exists!"),
output_field=CharField()
)
)
Run Code Online (Sandbox Code Playgroud)
relatedmodel_set你的外键在哪里related_name。
| 归档时间: |
|
| 查看次数: |
1419 次 |
| 最近记录: |