此解決方案針對內存需求進行了優化,因爲您認爲它很重要。它需要三個查詢。第一個查詢要求提交帖子,第二個查詢只適用於元組(id,post_id)。第三個過濾最新評論的細節。
from itertools import groupby, islice
posts = Post.objects.filter(...some your flter...)
# sorted by date or by id
all_comments = (Comment.objects.filter(post__in=posts).values('post_id')
.order_by('post_id', '-pk'))
last_comments = []
# the queryset is evaluated now. Only about 100 itens chunks are in memory at
# once during iterations.
for post_id, related_comments in groupby(all_comments(), lambda x: x.post_id):
last_comments.extend(islice(related_comments, 2))
results = {}
for comment in Comment.objects.filter(pk__in=last_comments):
results.setdefault(comment.post_id, []).append(comment)
# output
for post in posts:
print post.title, [x.comment for x in results[post.id]]
,但我認爲這將是快了很多數據庫後端的第二個和第三個查詢合併爲一個,因此立即要求的意見各個領域。無用的評論將被立即遺忘。
最快的解決方案是使用嵌套查詢。該算法與上面的算法類似,但所有內容均通過原始SQL實現。它僅限於PostgresQL等後端。
編輯
我同意,是不是對你有用
...預取加載到內存中數千條評論,其中99%將不會顯示。
因此,我寫了一個相對複雜的解決方案,其中99%將連續讀取而不加載到內存中。
EDIT
- 所有實施例僅用於您在棒POST_ID的條件[1,3,5]
- 在所有情況下創建(enything早些時候按類別等選擇的)關於字段註釋索引[ '後', 'PK']
A)嵌套查詢PostgreSQL的
SELECT post_id, id, text FROM
(SELECT post_id, id, text, rank() OVER (PARTITION BY post_id ORDER BY id DESC)
FROM app_comment WHERE post_id in (1, 3, 5)) sub
WHERE rank <= 2
ORDER BY post_id, id
如果我們不相信優化器,或者明確要求更少的內存。它應該只從索引中兩個內選擇,其是少得多的數據比從表:
SELECT post_id, id, text FROM app_comment WHERE id IN
(SELECT id FROM
(SELECT id, rank() OVER (PARTITION BY post_id ORDER BY id DESC)
FROM app_comment WHERE post_id in (1, 3, 5)) sub
WHERE rank <= 2)
ORDER BY post_id, id
b)與最老的顯示評論
過濾
from django.db.models import F
qs = Comment.objects.filter(
post__pk__in=[1, 3, 5],
post__oldest_displayed__lte=F('pk')
).order_by('post_id', 'pk')
pprint.pprint([(x.post_id, x.pk) for x in qs])
嗯,很不錯的...它是如何編譯(你已經按類別等較早選擇)通過Django?
>>> print(qs.query.get_compiler('default').as_sql()[0]) # added white space
SELECT "app_comment"."id", "app_comment"."text", "app_comment"."post_id"
FROM "app_comment"
INNER JOIN "app_post" ON ("app_comment"."post_id" = "app_post"."id")
WHERE ("app_comment"."post_id" IN (%s, %s, %s)
AND "app_post"."oldest_displayed" <= ("app_comment"."id"))
ORDER BY app_comment"."post_id" ASC, "app_comment"."id" ASC
備齊「oldest_displayed」由一個嵌套的SQL最初(和設置崗位爲零不到兩年的意見):
UPDATE app_post SET oldest_displayed = 0
UPDATE app_post SET oldest_displayed = qq.id FROM
(SELECT post_id, id FROM
(SELECT post_id, id, rank() OVER (PARTITION BY post_id ORDER BY id DESC)
FROM app_comment) sub
WHERE rank = 2) qq
WHERE qq.post_id = app_post.id;
我不知道'.select_related( '意見')'提取意見。 '.select_related'可以獲取ForeignKey的,OneToOne關係和反向OneToOne – Igor 2014-10-14 12:51:54
@Igor,呵呵,我不知道是這種情況。我猜[prefetch_related]的文檔(https://docs.djangoproject.com/en/1.6/ref/models/querysets/#prefetch-related)暗示這一點。感謝您的高舉。 – tino 2014-10-14 16:50:41
提取所有相關注釋時出現什麼問題?您以後可以在每篇文章中只使用前兩項。 'posts [0] .comments.all()'不會執行額外的查詢。這個問題是否有太多的相關查詢來預取它們? – 2014-10-17 13:20:54