2

在 Django ORM 中创建子查询很简单(只需将 aQuerySet作为另一个查询的一部分),但是该子查询是否可以引用“父”(外部,主)查询中的字段?

有关我要实现的目标的完整示例,请参阅此工作SQL Fiddle。我把它分解成两个问题(另一个在这里)。在这种情况下,我有一个模型Whole表示必须达到的值。几个Parts 以自己的(计算的)值对其做出贡献。我想检索所有尚未完成的sWhole(即不同于单个值的总和)。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(并且可以通过print qs.query结果确实作为单个查询运行来确认),但前提是两个查询彼此独立。在这里,子查询受父查询 ( w.id) 中的字段约束。我想过使用F(), Q(), 甚至extra, 但不知道该怎么做...

这是一个 SSCCE,如果有人想尝试一下:下载浏览它具有与上面链接的SQL 小提琴相同的模型和数据。


更新:对于我的特殊情况,我发现不需要做子查询,我可以使用group byand having(正如这个 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,如下面的回答所示)。

4

1 回答 1

2

我用最少的原始 SQL 设计的解决方案使用extraand where

  • 首先创建内部查询;用于extra指定自定义where组件,将受限字段与外部查询中的字段进行比较,因为它将出现在那里(可能需要对表名/别名进行硬编码):

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

    然后对其进行剩余的操作(在这种情况下,使用valuesandannotate进行单个字段的分组、聚合和返回)。

  • 然后在外部查询中包含内部查询(使用.query)生成的 SQL,也使用extraand where

    qs = Whole.objects.extra(where=['total_value != ({})'.format(qs1.query)])
    

调用中的代码片段extra可能不可移植(例如:一些后端使用!=,其他使用<>,引用表名的正确方式可能会有所不同,等等),但内部查询的其余部分应该是(因为它是由奥姆)。

结果查询对应于我正在寻找的内容(聚合部分除外,该部分在另一个问题中有所介绍)。为便于阅读而格式化的 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"
)
于 2013-03-18T17:57:43.563 回答