我想将Django中的查询传递给我的PostgreSQL数据库。当我使用大量的id过滤我的查询时,查询非常慢并且最多可达70秒。
在找到答案后,我看到this post为我的问题提供了解决方案,只需在ARRAY [ids]
中更改IN语句中的VALUES (id1), (id2), ...
。
我在pgadmin中使用原始查询测试了解决方案,查询从70秒到300毫秒......
如何在Django中执行相同的命令(即不使用id数组,但使用VALUES查询)?
答案 0 :(得分:5)
我发现了使用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>)
答案 1 :(得分: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
表达式传递 集 相同的效果。但是传递数组通常要简单得多。
详细说明:
答案 2 :(得分:1)
这是你问的第一件事吗?
relation_list = list(ModelA.objects.filter(id__gt=100))
obj_query = ModelB.objects.filter(a_relation__in=relation_list)
那将是&#34; IN&#34;命令,因为您首先通过将relation_list
投射到list
来评估obj_query.query
,然后在第二次查询中使用它。
如果你做的完全相同,Django只会进行一次查询,并为你做SQL优化。所以它应该更有效率。
如果您对在幕后发生的事情感到好奇,您始终可以看到您将使用{{1}}执行的SQL命令。
希望回答这个问题,对不起,如果它没有。
答案 3 :(得分:0)
要使自定义查找“ ineff”工作非常麻烦。 我可能已经解决了,但希望得到Django和Postgres专家的一些验证。
1)在ForeignKey字段(模型B)上“直接”使用
ModelA.objects.filter(ModelB__ineff=queryset_ModelB)
引发以下异常: “相关字段的查询无效:无效”
ForeignKey字段不能与自定义查找一起使用 这里报告了类似的问题: Custom lookup is not being registered in Django
2)在相关模型(ModelB.id)的pk字段上“间接”使用它
ModelA.objects.filter(ModelB__id__ineff=queryset_ModelB.values_list('id', flat=True))
引发以下异常: “只能将列表(而不是“元组”)连接到列表”
看着Django Traceback,我注意到rhs_params是一个元组。 但是,我们尝试在自定义查找中将其添加到lhs_params(列表)中。
因此我改变了:
params = lhs_params + rhs_params
进入:
params = lhs_params + list(rhs_params)
3)然后我收到了Postgres错误(至少我已经通过了Django ORM) “函数unnest(uuid)不存在” “提示:没有函数与给定的名称和参数类型匹配。您可能需要添加显式类型转换。”
我显然是通过更改sql来解决的:
来自:
return "%s IN (SELECT unnest(%s))" % (lhs, rhs), params
收件人:
return "%s IN (SELECT unnest(ARRAY(%s)))" % (lhs, rhs), params
因此,我的最终as_sql方法如下所示:
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 + list(rhs_params)
return "%s IN (SELECT unnest(ARRAY(%s)))" % (lhs, rhs), params
它似乎有效,并且确实比in__更快(已在Postgres中使用EXPLAIN ANALYZE测试)。 但是我想得到专家的验证,也许是欧文·布兰德斯特(Erwin Brandstetter)? 感谢您的输入。