为了账号安全,请及时绑定邮箱和手机立即绑定

Django ORM中的select_related和prefetch_related有什么区别?

/ 猿问

Django ORM中的select_related和prefetch_related有什么区别?

慕码人2483693 2019-10-05 11:12:54

在Django文档中,


select_related() “遵循”外键关系,在执行查询时选择其他相关对象数据。


prefetch_related() 对每个关系进行单独的查找,并在Python中执行“联接”。


“在python中进行连接”是什么意思?有人可以举例说明吗?


我的理解是,对于外键关系,使用select_related; 对于M2M关系,请使用prefetch_related。这个对吗?


查看完整描述

3 回答

?
青春有我

您的理解基本上是正确的。您可以使用select_related时,你将要选择的对象是一个对象,所以OneToOneField还是ForeignKey。您可以使用prefetch_related时,你会得到一个东西“设置”,那么ManyToManyFieldS作为你陈述或反向ForeignKey秒。为了阐明我的意思是“ reverse ForeignKeys”,这里有一个例子:


class ModelA(models.Model):

    pass


class ModelB(models.Model):

    a = ForeignKey(ModelA)


ModelB.objects.select_related('a').all() # Forward ForeignKey relationship

ModelA.objects.prefetch_related('modelb_set').all() # Reverse ForeignKey relationship

区别在于select_related执行SQL连接,因此从SQL Server将结果作为表的一部分返回。prefetch_related另一方面,执行另一个查询,因此减少了原始对象中的冗余列(ModelA在上面的示例中)。您可以使用prefetch_related任何可以使用的东西select_related。


需要权衡的是prefetch_related必须创建并发送ID列表以选择回服务器,这可能需要一段时间。我不确定交易中是否有很好的方法,但是我的理解是Django总是只发送一个列表并说SELECT ... WHERE PK IN(...,...,...)基本上。在这种情况下,如果预取的数据稀疏(例如,将美国国家对象链接到人们的地址),这可能会很好,但是,如果它们之间的关系更接近一对一,则会浪费大量通信资源。如有疑问,请尝试两者并查看哪种效果更好。


上面讨论的所有内容基本上都与与数据库的通信有关。但是,在Python方面prefetch_related具有额外的好处,即使用单个对象表示数据库中的每个对象。使用select_related重复的对象将在Python中为每个“父”对象创建。由于Python中的对象具有相当大的内存开销,因此这也是一个考虑因素。


查看完整回答
反对 回复 2019-10-05
?
繁星点点滴滴

两种方法可以达到相同的目的,从而放弃不必要的数据库查询。但是他们使用不同的方法来提高效率。


使用这两种方法的唯一原因是,当单个大型查询优于许多小型查询时。Django使用大型查询来抢先在内存中创建模型,而不是针对数据库执行按需查询。


select_related对每个查找执行联接,但将选择范围扩展为包括所有联接表的列。但是,这种方法有一个警告。


联接有可能使查询中的行数相乘。当您通过外键或一对一字段执行联接时,行数不会增加。但是,多对多联接没有此保证。因此,Django限制select_related了不会意外导致大规模联接的关系。


对于“ join in python”来说prefetch_related,应该比它还要令人震惊。它为要连接的每个表创建一个单独的查询。它使用WHERE IN子句过滤每个表,例如:


SELECT "credential"."id",

       "credential"."uuid",

       "credential"."identity_id"

FROM   "credential"

WHERE  "credential"."identity_id" IN

    (84706, 48746, 871441, 84713, 76492, 84621, 51472);

将每个表拆分为一个单独的查询,而不是执行可能包含太多行的单个联接。


查看完整回答
反对 回复 2019-10-05
?
春华秋衣

我会对您关于预取相关的评论“通常没有多大意义”提出异议。对于标记为唯一的FK字段,这是正确的,但是在多行具有相同FK值(作者,用户,类别,城市等)的任何地方,预取会减少Django和DB之间的带宽,但不会重复行。通常,它在数据库上使用的内存也较少。这些通常比单个额外查询的开销更为重要。鉴于这是一个相当普遍的问题的最佳答案,我认为应该在答案中注明。

查看完整回答
反对 回复 2019-10-05

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信