我想将Django中的查询传递给我的PostgreSQL数据库。当我使用大量的id过滤我的查询时,查询非常慢并且最多可达70秒。
在寻找答案后,我看到this post解决了我的问题,只需在ARRAY [ids]
的IN语句中更改VALUES (id1), (id2), ...
。
我在pgadmin中使用原始查询测试了解决方案,查询从70s到300ms ......
如何在Django中执行相同的命令(即不使用id数组,但使用VALUES查询)?
我找到了一个使用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>)
诀窍是以某种方式将数组转换为集合。
而不是(这种形式只适用于短阵列):
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
表达式传递集合相同的性能效果。但是传递数组通常要简单得多。
详细说明:
这是你问的第一件事的例子吗?
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命令,如果你很好奇发生了什么。
希望回答这个问题,对不起,如果没有。