Django有两个__in查询比检索单独的查询集慢

时间:2014-11-25 21:09:44

标签: python mysql django join django-queryset

我有一个相当复杂的数据库,由一个模型Bar组成,它可能有几个Spam,由ForeignKey关系实现(每个Spam知道哪个Bar它属于)。另一个模型Foo恰好有两个Bars。这是models.py的相关位:

class Bar(models.Model):
    g = models.IntegerField()
    ...

class Spam(models.Model):
    name = models.CharField(max_lendth=20)
    val = models.CharField(max_length=10)
    bar = models.ForeignKey(bar)

class Foo(models.Model):
    bar1 = models.ForeignKey(Bar, related_name='foo_bar_2')
    bar2 = models.ForeignKey(Bar, related_name='foo_bar_1')

我想知道哪个Foo有一对特定的bar,其中每一个都由Spams的一组标识。获得与bars匹配的两组很好并且速度相当快:

def get_bars_queryset(spam_tuples):
    bars = Bar.objects
    for name, val in spam_tuples:
        bars = bars.filter(spam__name=name, spam__val=val)
    return bars

    bars1 =  get_bars_queryset(spam_tuples=[('eggs', '5'), ('bacon', 'yes')])
    bars2 =  get_bars_queryset(spam_tuples=[('eggs', '1'), ('toast': '4'), ('milk', '2')])

但是,当我尝试以明显的方式检索我想要的Foo时:

foos = Foo.objects.filter(bar1__in=bars1, bar2__in=bars2)
表现非常糟糕。事实证明,分别检索两组Foo并找到它们的联合要快得多:

foos1 = Foo.objects.filter(bar1__in=bars1)
foos2 = Foo.objects.filter(bar2__in=bars2)

foo_bar1_ids = set(foos1.values_list('pk', flat=True))
foo_bar2_ids = set(foos2.values_list('pk', flat=True))
foo_id = foo_bar1_ids & foo_bar2_ids
foos = Foo.objects.filter(pk__in=foo_id)

我猜这是因为原生的Django方法没有有效地加入我的表。我怎么强迫它?

1 个答案:

答案 0 :(得分:0)

好吧,如果它对其他人有帮助,解决办法是强制评估bar查询集:

bars1 =  list(get_bars_queryset(spam_tuples=[('eggs', '5'), ('bacon', 'yes')]))
bars2 =  list(get_bars_queryset(spam_tuples=[('eggs', '1'), ('toast': '4'),
                                             ('milk', '2')]))
在使用

过滤Foos之前

foos = Foo.objects.filter(bar1__in=bars1, bar2__in=bars2)

这在文档(性能注意事项)中暗示:https://docs.djangoproject.com/en/1.7/ref/models/querysets/#in特别是MySQL的一个问题。感谢@ Minh-Hung Nguyen将我指向正确的方向。