【问题标题】:How to avoid distinct() when filtering on membership in multiple collections?过滤多个集合中的成员资格时如何避免 distinct()?
【发布时间】:2021-08-26 20:53:52
【问题描述】:

我正在处理 django 中一个相当慢的查询,该查询需要显示按多个组的成员身份过滤的用户。结果列表只需要包含唯一用户,这对于小数字来说很好,但是在返回处理大型查询集时就变成了一个很大的问题。不幸的是,在这个阶段修改数据库结构并不可行,因为这是一个拥有大量活跃用户群的实时产品。

当前设置外观的基本示例:

class Group( models.Model ):
   name = models.CharField(max_length=50)

class User( models.Model ):
   name= models.CharField( max_length=100)
   groups = models.ManyToManyField( Group, related_name='members' )

class UserDisplayModule( models.Model ):
   display_groups = models.ManyToManyField( Group, related_name='display_modules' )

一个视图通常会加载一个模块,然后通过分配的组列出与该模块关联的所有用户,例如:

class UserModuleMembers( ModelViewSet ):
    def get_queryset(self):
        user_module = self.get_module()
        return User.objects.filter(groups__in=module.display_groups.all()).distinct()[offset:offset+limit]

实际模型的列数要大一些,但关系确实如上。一个用户可能是多个组的成员,并且可以将多个组分配给一个模块,因此此处需要 distinct() 子句,因为如果单个用户在附加到一个模块。然后对生成的查询集进行分页。

上述内容对于小型应用程序来说效果很好,但我们正在快速发展,现在正在处理一个视图可能包含 100,000 多个用户的情况。即使有分页,这个查询也需要几秒钟才能返回。如果没有 distinct 子句,它会快很多,但由于多组的事情,我们会得到重复的记录。

底层数据库是 postgres,我尝试使用.distinct( 'name', 'id' ) 来限制考虑为“唯一性”的列,但这并没有明显的区别。

如果有人可以提出一种替代方法,我真的很想听听!

【问题讨论】:

  • 我更关心groups__in=module.display_groups.all()
  • 为什么不User.objects.filter(groups__display_modules=module).distinct()。这会产生一些简单的连接。
  • 噢……这是一个有趣的想法,我一定会尝试一下
  • 虽然原始方法只会产生子查询,所以我认为就连接而言应该没问题?
  • 好的,是的,速度快了一点,谢谢——这似乎指向了正确的方向

标签: python django postgresql django-rest-framework orm


【解决方案1】:

我更关心groups__in=module.display_groups.all() 组部分。虽然子查询可以像简单的 JOIN 一样快,但通常很多数据库都在处理子查询。

JOINs 的形式在一个条件下写这个可能会有所帮助。这也会使查询更简单,这通常意味着数据库可以更好地优化查询。

我们可以过滤:

User.objects.filter(<b>groups__display_modules=module</b>)<b>.distinct()</b>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-03-04
    • 1970-01-01
    • 1970-01-01
    • 2020-03-24
    • 2011-09-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多