【问题标题】:Django ORM: window function with subsequent filteringDjango ORM:带有后续过滤的窗口函数
【发布时间】: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表达式的问题。

标签: python django django-orm


【解决方案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 @.
    猜你喜欢
    • 2018-11-14
    • 2018-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-14
    • 2018-06-09
    • 1970-01-01
    相关资源
    最近更新 更多