在Postgres DB中使用大量id的Django查询过滤器

问题描述 投票:11回答:3

我想将Django中的查询传递给我的PostgreSQL数据库。当我使用大量的id过滤我的查询时,查询非常慢并且最多可达70秒。

在寻找答案后,我看到this post解决了我的问题,只需在ARRAY [ids]的IN语句中更改VALUES (id1), (id2), ...

我在pgadmin中使用原始查询测试了解决方案,查询从70s到300ms ......

如何在Django中执行相同的命令(即不使用id数组,但使用VALUES查询)?

arrays django postgresql parameter-passing
3个回答
6
投票

我找到了一个使用custom lookup建立@ erwin-brandstetter答案的解决方案

from django.db.models import Lookup
from django.db.models.fields import Field

@Field.register_lookup
class EfficientInLookup(Lookup):

    lookup_name = "ineff"

    def as_sql(self, compiler, connection):
        lhs, lhs_params = self.process_lhs(compiler, connection)
        rhs, rhs_params = self.process_rhs(compiler, connection)
        params = lhs_params + rhs_params
        return "%s IN (SELECT unnest(%s))" % (lhs, rhs), params

这允许像这样过滤:

MyModel.objects.filter(id__ineff=<list-of-values>)

2
投票

诀窍是以某种方式将数组转换为集合。

而不是(这种形式只适用于短阵列):

SELECT *
FROM   tbl t
WHERE  t.tbl_id = ANY($1);
-- WHERE  t.tbl_id IN($1);  -- equivalent

$1是数组参数。

您仍然可以传递一个类似于它的数组,但是不需要并加入。喜欢:

SELECT *
FROM   tbl t
JOIN   unnest($1) arr(id) ON arr.id = t.tbl_id;

或者你也可以保留你的查询,但用一个不需要它的子查询替换数组:

SELECT * FROM tbl t
WHERE  t.tbl_id = ANY (SELECT unnest($1));

要么:

SELECT * FROM tbl t
WHERE  t.tbl_id IN    (SELECT unnest($1));

与使用VALUES表达式传递集合相同的性能效果。但是传递数组通常要简单得多。

详细说明:


1
投票

这是你问的第一件事的例子吗?

relation_list = list(ModelA.objects.filter(id__gt=100))
obj_query = ModelB.objects.filter(a_relation__in=relation_list)

这将是一个“IN”命令,因为你首先通过将它投射到relation_list来评估list,然后在第二个查询中使用它。

相反,如果你做同样的事情,Django只会做一个查询,并为你做SQL优化。所以它应该更有效率。

你可以随时看到你将用obj_query.query执行的SQL命令,如果你很好奇发生了什么。

希望回答这个问题,对不起,如果没有。

© www.soinside.com 2019 - 2024. All rights reserved.