在Django查询中,使用Count函数和多个过滤器的最佳优化方式是什么
比如在学校里,有很多数据需要存储,比如“班级”、“学生”、“书籍”等等。而且,这里使用了一种叫做软删除的方式(软删除就是在删除的时候,只是把模型里的 is_deleted
改成 True
,并没有真的把数据删除)。在每个模型里,通常会有 create_time
(创建时间)、update_time
(更新时间)、is_delete
(是否删除)等字段。现在我的代码运行这个查询时,至少要花30秒才能完成。
那么,有什么更优化的方法可以用来查询 Count
,而且这个查询还需要满足很多条件呢?有时候我们还需要对同一个模型进行计数,但使用不同的过滤条件。
这里有一个在Django中的代码示例(检查每个学校的数据计数)。
# In this code, each model already implements softdelete manager query.
School.objects.annotate(
number_of_class=Count(
"class", filter=Q(class__is_deleted=False, class__create_time__lte=datetime, ...more filter), distinct=True
),
number_of_student=Count(
"student", filter=Q(student__is_deleted=False, student__create_time__lte=datetime, ...more filter), distinct=True
),
number_of_public_book=Count(
"book", filter=Q(book__is_deleted=False, book__create_time__lte=datetime, book__create_time__gte=datetime, book__type=1, ...more filter), distinct=True
),
number_of_private_book=Count(
"book", filter=Q(book__is_deleted=False, book__create_time__lte=datetime, book__create_time__gte=datetime, book__type__in=[2,3], ...more filter), distinct=True
)
more_count...
)
请帮忙提供你知道的所有查询代码,以及你认为在计数和过滤方面可以优化的地方。
1 个回答
0
因为你每次都在过滤掉已删除的书籍,所以你可以在所有计数中直接把这些书籍省略掉,方法是:
School.objects.filter(
book__is_deleted=False,
book__create_time__lte=datetime,
book__create_time__gte=datetime,
).annotate(
number_of_public_book=Count('book', filter=Q(book__type=1)),
number_of_private_book=Count('book', filter=Q(book__type__in=[2, 3])),
)
另外,不要在同一个查询中对不同的 JOIN
进行聚合操作:这样会导致记录数量激增,因为现在每本书和每个班级在每个学校都有一行记录,这样你就需要使用一个distinct=True
[Django-doc],这会让处理变得更慢。建议你对每个你连接的表单独进行查询。所以在这种情况下,针对书籍、班级等分别进行查询。在这样的查询中,你可以像上面的代码示例那样批量统计书籍的数量。
最后,在 is_deleted
和 create_time
上添加一个db_index=True
[Django-doc],这样可以提高性能。