2013-03-16 80 views
1

很简单创造Django的ORM(只使用一个QuerySet作为另一个查询的一部分),子查询,但有可能是子查询引用领域中的“家长”(外,主)查询?在Django子查询中,我可以引用“父”查询吗?

为了什么,我试图实现一个完整的例子,看到这个工作SQL Fiddle。我把它分解成两个问题(other one here)。在这种情况下,我有一个模型Whole,表示必须达到的值。有几个Part s以它们自己的(计算)的值作出贡献。我要检索的所有Whole S的具有尚未完成(即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做到这一点。我已经使用子查询的manyexamples__in(并且可以通过print qs.query确认结果确实跑了作为一个单一的查询),但只有当这两个查询是相互独立的观察。在这里,子查询受到父查询中的字段(w.id)的约束。我想用F()Q(),甚至extra的,但不能完全弄清楚该怎么办...

这里有一个SSCCE,万一有人想用它做实验:DownloadBrowse。它与上面链接的SQL fiddle具有相同的型号和数据。


更新:对我的具体情况,我发现没有必要做一个子查询,我可以只使用group byhaving(如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显示)。

回答

2

我用最少的生SQL设计的解决方案使用extrawhere

  • 首先创建内部查询;使用extra指定一个自定义where组成部分,限制外地在外部查询比较的一个,,因为它会出现在那里(可能需要硬编码表名/别名):

    qs1 = Part.objects.extra(where=['whole_id = "applabel_whole"."id"'])... 
    

    然后使其余的操作(在这种情况下,使用valuesannotate来分组,汇总并返回单个字段)。

  • 然后包括内部查询的生成的SQL在外部查询(使用.query),还使用extrawhere

    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" 
) 
相关问题