prefetch_related
states = State.objects.prefetch_related('country', 'cities').all()
相反地, prefetch_related 的功能是收集关联对象的全部id值,一次性批量获取到它们,然后透明的附到相应的对象。这种方式最好的一个地方是能用在一对多关系中,比如本例中的州跟市。
下面是这种方式生成的SQL:
SELECT ... FROM "locations_state" ORDER BY "locations_state"."name" ASC
SELECT ... FROM "locations_country" WHERE "locations_country"."id" IN (1)
SELECT ... FROM "locations_city"
WHERE "locations_city"."state_id" IN (1, 2, 3)
ORDER BY "locations_city"."name" ASC
SELECT ... FROM "locations_country" WHERE "locations_country"."id" IN (1)
SELECT ... FROM "locations_city"
WHERE "locations_city"."state_id" IN (1, 2, 3)
ORDER BY "locations_city"."name" ASC
这样2N+1就变成3了。把N扔掉是个大进步。3 * 20ms总是会比(2 * 50 + 1) * 20ms 小,甚至比用select_related时的 (50 + 1) * 20ms也小。
states = State.objects.select_related('country') \
.prefetch_related('cities').all()
.prefetch_related('cities').all()
上面这个例子对国家跟市都采用了prefetch。前面说过这里的州都属同一国家,用select_related获得州记录时,这意味着要取到并处理这一国家记录N次。相反,用prefetch_related只要取一次。而这样会引入一次额外的数据库往返,有没有可能综合两种方式,你得在你的机器及数据上试试。然而,在本例中同时用select_related 和 prefetch_related可以将时间降到2 * 20ms,这可能会比分3次查询要快,但也有很多潜在因素要考虑。