查询优化递归多对多查询

时间:2011-07-06 13:52:15

标签: django django-models

我正在使用一个小树/图形包(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个以上的孩子时,它们可以快速加起来。最终,将有成千上万的db条目。现在,每个查询的平均值为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()导致了点击。是否有另一种方法可以在不使用callable属性的情况下获取所有多对多孩子?

1 个答案:

答案 0 :(得分:1)

您必须根据自己的情况在自己的环境中进行测试。通常建议使用select_related,但是如果会有很多递归级别,那么一个大型查询通常比多个查询慢。

孩子的数量并不重要,递归的水平是最重要的。如果你做3个左右,select_related()可能会更好,但远远超过这个可能会导致减速。插件作者可能这样做是为了允许多个级别的递归,因为只有少数几个才会真正受到伤害,而且这只是一些额外的查询。