使用嵌套聚合优化 Django QuerySet

问题描述 投票:0回答:1

我正在努力优化复杂的 Django 查询,其中我需要跨多个相关模型执行嵌套聚合和条件注释。我想根据用户与帖子的互动来获取前 5 位最活跃的用户,同时还计算不同类型的参与度指标(例如视图、评论和点赞)。

我的模特:

class User(models.Model):
    name = models.CharField(max_length=100)

class Post(models.Model):
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    title = models.CharField(max_length=255)
    created_at = models.DateTimeField()

class Engagement(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    post = models.ForeignKey(Post, on_delete=models.CASCADE)
    type = models.CharField(max_length=50)  # 'view', 'like', 'comment'
    created_at = models.DateTimeField()

这是我的代码的样子:

from django.db.models import Count, Q

some_date = ...

top_users = (
    User.objects.annotate(
        view_count=Count('engagement__id', filter=Q(engagement__type='view', engagement__created_at__gte=some_date)),
        like_count=Count('engagement__id', filter=Q(engagement__type='like', engagement__created_at__gte=some_date)),
        comment_count=Count('engagement__id', filter=Q(engagement__type='comment', engagement__created_at__gte=some_date)),
        total_engagements=Count('engagement__id', filter=Q(engagement__created_at__gte=some_date))
    )
    .order_by('-total_engagements')[:5]
)

它可以工作,但是查询性能并不理想。对于大型数据集,这种方法会导致查询执行时间变慢,我想知道使用多个

Count
注释和
filter
条件是否有效。

是否有更优化的方法来编写此查询,或者我应该考虑提高性能的任何最佳实践,特别是在处理大量数据时?任何见解或建议都会非常有帮助!

django django-views django-queryset django-q
1个回答
0
投票

它可以工作,但是查询性能并不理想。对于大型数据集,这种方法会导致查询执行时间变慢,我想知道使用带有过滤条件的多个 Count 注释是否有效。

效率不是很高。

filter=…
 [Django-doc] 方法作为
CASE … WHEN …
实现,因此通常意味着数据库将首先考虑 all 参与,然后过滤掉不满足过滤器的参与在线性扫描中。

但是,如果我们不想在

some_date
之后返回没有任何参与的用户,我们可以通过过滤 JOIN 来提高效率:

top_users = (
    User.objects.filter(engagement__created_at__gte=some_date)
    .annotate(
        view_count=Count('engagement__id', filter=Q(engagement__type='view')),
        like_count=Count('engagement__id', filter=Q(engagement__type='like')),
        comment_count=Count(
            'engagement__id', filter=Q(engagement__type='comment')
        ),
        total_engagements=Count('engagement__id'),
    )
    .order_by('-total_engagements')[:5]
)

并在 db_index=True 字段上添加

created_at [Django-doc]

class Engagement(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    post = models.ForeignKey(Post, on_delete=models.CASCADE)
    type = models.CharField(max_length=50)  # 'view', 'like', 'comment'
    created_at = models.DateTimeField(db_index=True)

注意:通常使用

settings.AUTH_USER_MODEL
 [Django-doc] 来引用用户模型比使用
User
模型 [Django- doc]
直接。有关更多信息,您可以参阅文档的引用
User
模型
部分[Django-doc]

© www.soinside.com 2019 - 2024. All rights reserved.