在Django查询中,使用Count函数和多个过滤器的最佳优化方式是什么

1 投票
1 回答
39 浏览
提问于 2025-04-14 16:23

比如在学校里,有很多数据需要存储,比如“班级”、“学生”、“书籍”等等。而且,这里使用了一种叫做软删除的方式(软删除就是在删除的时候,只是把模型里的 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_deletedcreate_time 上添加一个db_index=True [Django-doc],这样可以提高性能。

撰写回答