带窗口函数的Django ORM子查询

时间:2018-06-04 21:38:10

标签: django orm window subquery max

我试图用Django的ORM进行此查询:

SELECT
  id,
  pn,
  revision,
  description
FROM (SELECT
        id,
        pn,
        revision,
        MAX(revision)
        OVER (
          PARTITION BY pn ) max_rev,
        description
      FROM table) maxarts
WHERE revision = max_rev

结果需要是一个查询集,我已经尝试过我所知道的Window / OuterRef / Subquery的每个组合都没有成功。 我必须使用原始查询吗?

提前致谢 马可

编辑#1:

我会尝试更好地解释,我有一个看起来像这样的模型:

class Article(models.Model):
  pn = models.CharField()
  revision = models.CharField()
  description = models.CharField()

  class Meta:
    unique_together = [("pn", "revision"), ]

数据类似于:

pn1    rev1    description
pn1    rev2    description
pn2    rev1    anotherdescription
pn1    rev3    description
pn2    rev2    anotherdescription

我需要一个只包含Max(" revision")值的查询集,每次用户对该对象进行修改时,该值都会递增。 我希望现在更清楚了。谢谢!

编辑#2

如我所知,我正在写我已经尝试过的东西:

使用第一条消息中编写的查询的原始SQL,仅选择id字段并将其作为id__in = ids传递给ORM。地狱慢,无法使用。

声明了一个用作过滤器的WIndow函数:

Article.objects.annotate(max_rev=Window(expression=Max("revision"), partition_by=F("pn"))).filter(revision=F("max_rev"))

但是Django抱怨我不能在where子句中使用窗口函数(这是正确的)。

然后我试图将窗口用作子查询:

window_query = Article.objects.annotate(max_rev=Window(expression=Max("revision"), partition_by=F("pn")))
result = Article.objects.filter(revision= Subquery(window_query)

我也尝试使用OuterRef,使用max_rev注释作为连接,没有运气。 我没有想法!

2 个答案:

答案 0 :(得分:0)

因此,每次针对文章进行修订时,都会在表格中创建一行?

如果是这样,您需要做的就是执行一个计数查询,该查询计算所有行并根据' pn'领域。如果您想使用Max功能,那么我建议您更换“pn'包含IntegerFieldDecimalField而非使用CharField的字段。虽然取决于您的应用程序所处的位置,但这可能非常困难。

from django.db.models import Count

Article.objects.values('pn').annotate(maxvalues=Count('pn'))

答案 1 :(得分:0)

我认为通过使用 FirstValue 而不是 Max,您可以获得您想要的,与您拥有的没有太大区别:

>>> window_query = Article.objects.annotate(max_id=Window(
        expression=FirstValue("id"),
        partition_by=F("pn"),
        order_by=F("revision").desc()
    )).values("max_id")
>>> list(Article.objects.filter(id__in=Subquery(window_query)))
[<Article: Article object (4)>, <Article: Article object (5)>]

这会产生如下 SQL:SELECT * FROM articles_article WHERE id IN (SELECT FIRST_VALUE(id) OVER (PARTITION BY pn ORDER BY revision DESC) AS max_id FROM articles_article)

子查询表示按修订版本降序对窗口进行排序,按 pn 分区,并从每个分区中获取第一个 ID;然后我们在父查询中使用它来获取这些 ID 的相关文章。

在 PostgreSQL 上,你也可以这样做:

>>> Article.objects.order_by('pn', '-revision').distinct('pn')
<QuerySet [<Article: Article object (4)>, <Article: Article object (5)>]>

这会产生类似 SELECT DISTINCT ON (pn) * FROM articles_article ORDER BY pn ASC, revision DESC 的 SQL。