Django ORM:带有后续过滤的窗口函数

Posted

技术标签:

【中文标题】Django ORM:带有后续过滤的窗口函数【英文标题】:Django ORM: window function with subsequent filtering 【发布时间】:2019-01-02 04:21:28 【问题描述】:

回答this question,我found out 不允许窗口函数与过滤器组合(从技术上讲,它们是,但过滤器子句会影响窗口)。有一个提示将窗口函数包装在内部查询中,因此最终的 SQL 看起来像这样(据我了解):

SELECT * FROM (
    SELECT *, *window_function* FROM TABLE)
WHERE *filtering_conditions*

问题是:如何用 Django ORM 编写这个查询?

【问题讨论】:

我遇到了同样的问题。在 postgres 中是可能的(参见文档底部),但我不知道如何将其转换为 ORM。有人有想法吗? @ilse2005 给定的表达式更像是 SELECT ... FROM (SUBQUERY_EXPRESSION) as foo 形式的 子查询。幸运的是,Django 有 Subquery(...) 表达式,但它不提供指定 FROM 子句的功能 据我了解,这是How to get a subquery in FROM clause in Django ORM?的问题,而不是Window表达式的问题。 【参考方案1】:

另一种解决方案是公用表表达式 (CTE),在 django-cte 的帮助下,您可以实现您想要的:

cte = With(
    YouModel.objects.annotate(
        your_window_function=Window(...),
    )
)

qs = cte.queryset().with_cte(cte).filter(your_window_function='something')

大致翻译为:

WITH cte as (
    SELECT *, WINDOW(...) as your_window_function
    FROM yourmodel
) 
SELECT * 
FROM cte
WHERE cte.your_window_function = 'something'

【讨论】:

【参考方案2】:

您需要使用原始查询。为了同时进行多个查询。更多信息django documentation

for p in Person.objects.raw('''
    SELECT * FROM (SELECT *, *window_function* FROM TABLE)
    WHERE *filtering_conditions*'''):
    print(p)
# John Smith
# Jane Jones

您可以做的其他事情如下。

模型.py

class Category(models.Model):
    name = models.CharField(max_length=100)


class Hero(models.Model):
    # ...
    name = models.CharField(max_length=100)
    category = models.ForeignKey(Category, on_delete=models.CASCADE)

    benevolence_factor = models.PositiveSmallIntegerField(
        help_text="How benevolent this hero is?",
        default=50
    )

querySet.py

hero_qs = Hero.objects.filter(category=OuterRef("pk"))
.order_by("-benevolence_factor")

Category.objects.all()
.annotate(most_benevolent_hero=Subquery(hero_qs.values('name')[:1]))

生成的 SQL 如下所示..

SELECT "entities_category"."id",
   "entities_category"."name",

  (SELECT U0."name"
   FROM "entities_hero" U0
   WHERE U0."category_id" = ("entities_category"."id")
   ORDER BY U0."benevolence_factor" DESC
   LIMIT 1) AS "most_benevolent_hero"
FROM "entities_category"

【讨论】:

谢谢!但不幸的是,使用原始 sql 不是一种选择。对于我的用例来说,添加额外的字段也不是一个很好的解决方案。【参考方案3】:

There are developers interested in solving it 但现在用 ORM 是不可能的。

一个建议的解决方案是添加一个QuerySet.subquery().wrap() 方法,推送子查询中的查询集,以便随后对其进行过滤。

【讨论】:

您能详细说明一下吗?你有关于过滤带有窗口函数注释的查询集的示例吗? ORM 目前不支持它,但想法是添加一个 subquery 方法,将当前查询集包装在 SELECT * FROM (queryset_query) as window_subquery 中,以便后续过滤器可以针对 @987654327 生成 WHERE @.

以上是关于Django ORM:带有后续过滤的窗口函数的主要内容,如果未能解决你的问题,请参考以下文章

在 django ORM 中使用 postgresql 窗口函数的干净方法?

Django ORM group by,并找到每个组的最新项目(窗口函数)

带有过滤器的pyspark窗口函数

NX二次开发-通过获取窗口句柄方式来设置类型过滤器EnumChildWindows

Django ORM:按对象列表过滤

Django 窗口注解,使用组合式分句。