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

在Django文档中,

select_related()“跟随”外键关系,在执行查询时select其他相关对象数据。

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

“在python中join”是什么意思? 有人可以用一个例子来说明吗?

我的理解是,对于外键关系,使用select_related; 对于M2M关系,使用prefetch_related。 它是否正确?

你的理解大多是正确的。 当您要select的对象是单个对象时,可以使用select_related ,因此可以使用OneToOneFieldForeignKey 。 当你要得到一组“事物”时,你使用prefetch_related ,所以ManyToManyField就像你所说的那样,或者是反向的ForeignKey 。 只是澄清我的意思是“反向外键”这里是一个例子:

 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服务器表的一部分返回。 另一方面, prefetch_related执行另一个查询,因此减less了原始对象(上ModelA中的ModelA中的冗余列。 您可以将prefetch_related用于您可以使用select_related for的任何内容。

权衡是prefetch_related必须创build并发送一个ID列表以select回服务器,这可能需要一段时间。 我不确定是否有一个很好的方式在事务中这样做,但我的理解是,Django总是发送一个列表,并说SELECT … WHERE pk IN(…,…,…)基本上。 在这种情况下,如果预取的数据是稀疏的(假设美国州对象链接到人们的地址),这可能是非常好的,但是如果它更接近一对一,这可能会浪费大量的通信。 如果有疑问,试试看看哪个更好。

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

两种方法达到相同的目的,放弃不必要的分贝查询。 但他们使用不同的方法来提高效率。

使用这两种方法的唯一原因是单个大型查询比许多小型查询更可取。 Django使用大型查询来预先在内存中创build模型,而不是对数据库执行按需查询。

select_related使用每个查找执行一个连接,但扩展select以包含所有连接表的列。 但是这种方法有一个警告。

连接有可能将查询中的行数相乘。 当您通过外键或一对一字段执行连接时,行数不会增加。 但是,多对多连接没有这个保证。 所以,Django将select_related限制为不会意外导致大量连接的关系。

prefetch_related“join in python”会更令人担忧。 它为每个要连接的表创build一个单独的查询。 它使用WHERE IN子句过滤每个表,如下所示:

 SELECT "credential"."id", "credential"."uuid", "credential"."identity_id" FROM "credential" WHERE "credential"."identity_id" IN (84706, 48746, 871441, 84713, 76492, 84621, 51472); 

每个表可以分成一个单独的查询,而不是执行一个有太多行的单个连接。