2011-07-06 86 views
1

我正在使用一個小樹/圖包(django_dag),它爲我的模型提供了一個多對多的引用自身的子字段。基本結構可以顯示爲以下型號針對遞歸多對多查詢的查詢優化

#models 
class Foo(FooBase): 
    class Meta: 
     abstract = True 

    children = models.ManyToManyField('self', symmetrical = False, 
             through = Bar) 

class Bar(): 
    parent = models.ForeignKey(Foo) 
    child = models.ForeignKey(Foo) 

所有的模型和包的所有功能都很好。 FooBase向模型添加了各種功能,包括遞歸地查找Foo的所有孩子以及孩子的孩子等等。

我所關注的是內FooBase以下功能:

def descendants_tree(self): 
    tree = {} 
    for f in self.children.all(): 
     tree[f] = f.descendants_tree() 
    return tree 

它輸出類似{Foo1:{}和Foo2:{Child_of_Foo2:{Child_of_Child_of_Foo2:{}}}}其中的後代處於嵌套字典。

警報讀取器可能會注意到此方法爲每個孩子調用一個新的查詢。 雖然這些db命中非常快,但當可能有50個以上的孩子時,它們可以快速加起來。最終,將會有數以萬計的數據庫條目。現在,每個查詢平均0.6毫秒,行計數將近2000.

是否有更有效的方法來做這個嵌套查詢?

在我看來,事先做一個select_related()。all()會把它歸結爲一個查詢,但是在將來會有麻煩。什麼時候一個大的問題比許多小問題更好或更糟?

---編輯---

這裏就是我想要測試select_related().all()選項有,但它仍然困擾着每個迭代:

all_foo = Foo.objects.select_related('children').all() 
def loop(baz): 
    tree = {} 
    for f in all_foo.get(id = baz).children.all() 
     tree[f] = loop(f) 
    return tree 

我假設children.all()導致命中。是否有另一種方法可以在不使用可調用屬性的情況下獲得所有多對多兒童?

回答

1

你必須在自己的環境下根據自己的情況進行測試。通常總是推薦使用select_related,但在存在多個遞歸級別的情況下,該大型查詢通常比多個查詢慢。

孩子的數量並不重要,遞歸的水平是最重要的。如果你在做3左右,select_related()可能會更好,但比這更可能導致減速。插件作者很可能以這種方式來實現許多遞歸級別,因爲只有少數幾個纔會真正受到傷害,而這只是一些額外的查詢。

+0

感謝您的信息。一般來說,大多數孩子只會有1-2級的遞歸,而有些則會有3-4級。我不知道這是否合情合理,但要與家庭模式保持一致:一位「父母」可能有30個孩子,10個孫子女和5個曾孫。數據比較深。現在我正在嘗試使用'all()'查詢進行比較。 –

+0

如果是這種情況,在你的情況下使用'select_related()'可能會更有意義。 –

+0

我只是想調整我的循環,它仍然在爲每個迭代做一個命中,而不是使用'cached'select_related()'查詢,所以我沒有做正確的事 –