django orm-如何使用values()执行复杂的group by.annotate().values()

w1jd8yoj  于 2021-06-21  发布在  Mysql
关注(0)|答案(1)|浏览(401)

我正在尝试将尽可能多的原始sql转换为使用django orm,但遇到了一个障碍。我正在尝试执行类似于此的查询:

SELECT table.x,
      MAX(table.y) AS y,
      table.group_category,
      table.group_number,
FROM table
GROUP BY table.group_category, table.group_number

到目前为止,我一直在尝试的是这种排列:

q = MyModel.objects\
    .filter(**filter_kwargs)\
    .values('group_category', 'group_number')\
    .annotate(y=Max('y'))\
    .values('x','y','group_category','group_number')

然而,这似乎不起作用。如果我排除最后一个 values() ,它产生以下(大致):

SELECT MAX(table.y) AS y,
      table.group_category,
      table.group_number,
FROM table
GROUP BY table.group_category, table.group_number

它不选择 table.x . 但如果我包括最后一个 values() ...

SELECT table.x,
      MAX(table.y) AS y,
      table.group_category,
      table.group_number,
FROM table
GROUP BY x, y, table.group_category, table.group_number

it分组依据 x, y . 所以很明显,所有的值都被替换了,annotate使用queryset给定的任何值(因为它的计算是惰性的?)。关于聚合和值的文档似乎表明,按此顺序执行这两个值函数将产生预期效果,我发现一个writeup(2013年)也表明了这一点。我做错什么了吗?这在django orm中仍然可能吗?有没有什么方法可以不使用extra()或原始sql来实现这一点?为了演示的目的,我尽量使这个例子保持简单,但是我的实际问题涉及到一个连接。这会让事情复杂化吗?

更新1

我能够找到它,但是,它仍然不能产生我想要的最佳版本的sql查询。为了得到所需的结果,我改为执行一个查询 MAX(table.y) ,然后使用 __in 对子查询的值。子查询执行分组。

filtered = MyModel.objects.filter(**filter_kwargs)

subq = filtered\
    .values('group_category', 'group_number')\
    .annotate(y=Max('y'))\
    .values_list('y', flat=True)

q = filtered\
    .filter(y__in=subq)\
    .values('x','y','group_category','group_number')

正如我所说的,这样做可以让我得到我需要的结果。问题是,它比只选择不同于groupby的查询要慢得多,因为它创建了一个相对庞大的子查询。我现在还没有将此标记为答案,因为它仍然无法生成与我真正想要的匹配的查询。相反,它看起来像这样:

SELECT table.x,
      table.y,
      table.group_category,
      table.group_number,
FROM table
WHERE y IN 
    (SELECT MAX(U0.y) AS y
    FROM table U0
    GROUP BY U0.group_category, U0.group_number)

此外,看起来我甚至不能使用extra(),因为它类似地只向select子句添加已经是queryset的一部分的列,即。 values() .

更新2

结果证明,我的凌乱解决方法不起作用,因为它获取所有y(1行)的最大值并返回它,而不是将它们按组\类别和组\编号分组并使用 MAX 他们的 y 是的,所以我回到绘图板上。

camsedfj

camsedfj1#

您似乎想要的是计算最大值,但返回所有行而不进行任何分组。这就是窗口函数的用途(可从django 2.0获得):

models = MyModel.objects.annotate(max=Window(
    expression=Max('y'),
    partition_by=[F('group_category'), F('group_number')],))

但是为什么你的方法 GROUP BY 工作?
在原始查询中,原因django(和数据库;您引用的sql将引发语法错误)坚持按 x 如果你分组的话 category 以及 number ,您可能有几个 x 一个分组行的值 category 以及 number . db应该选择哪一个?它不能为你做出这样的选择。
如果 x 不重要,你可以忽略它。如果它很重要,但对于一组 category 以及 number ,然后按 x 不会伤害你。如果有不同的 x 值,并且它们很重要,您需要决定选择哪一个(并相应地告诉db)。同样的道理 y .

相关问题