2012-12-31 77 views
7

此代碼當前正在執行約50 SQL查詢:如何減少查詢次數?

c = Category.objects.all() 

categories_w_rand_books = [] 

for category in c: 
    r = Book.objects.filter(author__category=category).order_by('?')[:5] 

    categories_w_rand_books.append((category, r)) 

我需要使用的查詢的數量減少到最低限度,以加快東西,不會造成服務器負載。

基本上,我有三種模式:類別,作者,書。作者屬於類別(不是書籍),我需要獲得所有類別的列表,每個類別下有5本隨機書籍。

+0

不設法得到它在代碼方面,而是試圖監視數據庫... – 2012-12-31 02:49:49

+1

更多的代碼可能是有用的(那是什麼'e'?)。你能否也請包括幾個重複的查詢? –

+0

嘗試優化查詢(加入他們)並將結果保存到變量中... – Raptor

回答

2

如果你喜歡單的查詢和使用MySQL,檢查由@Crazyshezy在他的評論提供the excellent link
對於PostgreSQL後端,一個可能的查詢(假設有非空的FK關係從BookAuthorAuthorCategory):

SELECT * FROM (
    SELECT book_table.*, row_number() OVER (PARTITION BY category_id ORDER BY RANDOM()) AS rn 
    FROM book_table INNER JOIN author_table ON book_table.author_id = author_table.id 
) AS sq 
WHERE rn <= 5 

然後,您可以把它包裝一個RawQuerySet內得到Book實例

from collections import defaultdict 
qs = Book.objects.raw("""The above sql suited for your tables...""") 
collection = defaultdict(list) 
for obj in qs: 
    collection[obj.category_id].append(obj) 

categories_w_rand_books = [] 
for category in c: 
    categories_w_rand_books.append((category, collection[category.id])) 

您可能不希望爲每個請求直接運行此查詢而無需進行某些緩存。

此外,你的代碼產生至多50 * 5 = 250 Book S,隨機的,我只是不知道爲什麼,因爲它似乎過多,單頁。項目是否顯示爲選項卡或其他內容?也許你可以通過做Ajax來減少SQL的數量,或者簡化要求?

更新

要使用book.author瓦特/ O觸發比另一個更查詢,嘗試prefetch_related_objects

from django.db.models.query import prefetch_related_objects 
qs = list(qs) # have to evaluate at first 
prefetch_related_objects(qs, ['author']) 
# now instances inside qs already contain cached author instances, and 
qs[0].author # will not trigger an extra query 

上面的代碼預取作者在批次,並將它們填充到qs。這只是增加了另一個查詢。

+0

您的代碼將查詢縮減爲大約一半。但是我的模板是這樣的:{在類別%%,持續類書籍} { 在書本%%,持續書} ... {{book.author}} ... {%ENDFOR%} .. {%endfor%} - 因此Django仍然要去數據庫讀取{{book.author}} –

+0

@adamsilver嘗試'prefetch_related_objects'或手動執行讀取和填充過程。 – okm

+0

這是返回None:raw_qs = list(qs) eee = prefetch_related_objects(raw_qs,['author']) print eee –

1

我不確定這是否會對您有所幫助,因爲我不知道您的問題的細節和上下文,但使用order_by('?')效率非常低,特別是某些數據庫後端。

對於有點隨機性我用這種方式來顯示實體,使用自定義過濾器:

@register.filter 
def random_iterator(list, k): 
    import random 
    class MyIterator: 
     def __init__(self, obj, order): 
      self.obj=obj 
      self.cnt=0 
      self.order = order 

     def __iter__(self): 
      return self 

     def next(self): 
      try: 
       result=self.obj.__getitem__(self.order[self.cnt]) 
       self.cnt+=1 
       return result 
      except IndexError: 
       raise StopIteration 

    if list is None: 
     list = [] 
    n = len(list) 
    k = min(n, k) 
    return MyIterator(list, random.sample(range(n), k)) 

在我的Django視圖中的代碼是這樣的:

RAND_BOUND = 50  
categories = Category.objects.filter(......)[RAND_BOUND] 

而且,我以這種方式在我的模板中使用它:

{% for cat in categories|random_iterator:5 %} 
<li>{{ cat }}</li>    
{% endfor %} 

此代碼將選擇5個隨機類別(r推出)一組RAND_BOUND。 這不是完美的解決方案,但希望它有幫助。