在Django ORM中创建子查询很简单(只使用QuerySet
作为另一个查询的一部分),但该子查询是否可以引用“父”(外部,主要)查询中的字段?< / p>
有关我正在尝试实现的完整示例,请参阅此工作SQL Fiddle。我将其分解为两个问题(other one here)。在这种情况下,我有一个模型Whole
,表示必须达到的值。有几个Part
以自己的(计算)值为其贡献。我想检索已经完成不的所有Whole
(即total_value
与各个值的总和不同)。
select w.*
from whole w
where w.total_value != (
select sum(value expression)
from part p
where p.whole_id = w.id
group by p.whole_id
);
我不知道如何(或者甚至可能)使用Django ORM来做到这一点。我使用__in
看到了many examples子查询(并且可以通过print qs.query
确认结果确实作为单个查询运行),但仅当两个查询都是相互独立。这里,子查询受父查询(w.id
)中的字段约束。我想过使用F()
,Q()
,甚至是extra
,但还是不知道该怎么办......
这是一个SSCCE,如果有人想要试验它:Download或Browse。它与上面链接的SQL fiddle具有相同的模型和数据。
更新:对于我的特定情况,我发现不需要执行子查询,我可以使用group by
和having
(作为this SQL Fiddle示出):
q = Q(part__isnull=True) | ~Q(partial=F('total_value'))
qs = Whole.objects.annotate(partial=Sum(...)).filter(q).distinct()
# And if total_value can be zero:
qs = qs.exclude(part__isnull=True, total_value=0)
子查询的一般情况仍然没有解决(没有使用一些原始SQL,如my answer below所示)。
答案 0 :(得分:2)
我使用最少原始SQL设计的解决方案使用extra
和where
:
首先创建内部查询;使用extra
指定自定义where
组件,将受限字段与外部查询中的字段进行比较,将显示在那里(可能需要对表名进行硬编码/别名):
qs1 = Part.objects.extra(where=['whole_id = "applabel_whole"."id"'])...
然后对其进行剩余的操作(在这种情况下,使用values
和annotate
进行分组,聚合和返回单个字段)。
然后在外部查询中包含内部查询的生成SQL(使用.query
),同时使用extra
和where
:
qs = Whole.objects.extra(where=['total_value != ({})'.format(qs1.query)])
extra
调用中的代码片段可能无法移植(例如:某些后端使用!=
,其他人使用<>
,引用表名的正确方法可能不同,等等),但内部查询的其余部分应为(因为它是由ORM生成的)。
结果查询对应于我正在寻找的内容(除了the other question中涵盖的聚合部分)。为便于阅读而格式化的SQL:
>>> qs1 = Part.objects.extra(
where=['whole_id = "aggregation_subquery_whole"."id"']
).values('whole_id').annotate(sum=Sum('before__value')).values('sum')
>>> qs = Whole.objects.extra(where=['total_value != ({})'.format(qs1.query)])
>>> print qs.query
SELECT "aggregation_subquery_whole"."id",
"aggregation_subquery_whole"."total_value"
FROM "aggregation_subquery_whole"
WHERE total_value != (
SELECT SUM("aggregation_subquery_sequence"."value") AS "sum"
FROM "aggregation_subquery_part"
LEFT OUTER JOIN "aggregation_subquery_sequence" ON
("aggregation_subquery_part"."before_id" =
"aggregation_subquery_sequence"."id")
WHERE whole_id = "aggregation_subquery_whole"."id"
GROUP BY "aggregation_subquery_part"."whole_id"
)