【问题标题】:Query with annotated aggregation over the same model在同一模型上使用带注释的聚合进行查询
【发布时间】:2021-04-08 12:01:24
【问题描述】:

我有以下型号:

class P(models.Model):
    name = models.CharField(max_length=30,  blank=False)


class pr(models.Model):
    p = models.ForeignKey(P, on_delete=models.CASCADE, related_name='cs')
    r = models.CharField(max_length=1)
    c = models.ForeignKey(P, on_delete=models.CASCADE, related_name='ps')
    rc = models.PositiveSmallIntegerField()

    class Meta:
        unique_together = (('p', 'c'),)

和数据:

"id","name"
69,"Hunter"
104,"Savannah"
198,"Adrian"
205,"Andrew"
213,"Matthew"
214,"Aiden"
218,"Madison"
219,"Harper"
---
"id","r","rc","c_id","p_id"
7556,"F",1,219,213
7557,"M",1,219,218
7559,"H",3,218,213
7572,"F",1,214,213
7573,"M",1,214,218
7604,"F",1,198,213
7605,"M",1,198,218
7788,"H",3,104,205
7789,"F",1,104,213
7790,"M",1,104,218
7866,"M",1,69,104
7867,"F",1,69,205

对于 pr 模型实例,我需要找到关联的 p_id 存在多少行。 这些查询会产生预期的结果:

ro = pr.objects.get(pk=7604)
cntp = pr.objects.filter(Q(p = ro.p) | Q(c = ro.p)).count()

上面的查询会打数据库2次,我想打数据库一次所以我写了这个查询:

ro = pr.objects.filter(pk=7604).annotate(cntp=Subquery(pr.objects.filter(Q(p = OuterRef('p')) | Q(c = OuterRef('parent'))).count()))

该查询生成错误“此查询集包含对外部查询的引用,并且只能在子查询中使用。”所以使用Simple Subquery with OuterRef中提到的方法:

ro = pr.objects.filter(pk=7604).annotate(cntp=Subquery(pr.objects.filter(Q(p = OuterRef('p')) | Q(c = OuterRef('c'))).annotate(count=Count('pk'))))

此查询再次生成另一个错误“子查询必须只返回一列”! 可以使用 Django ORM 来生成预期的结果吗?

环境:Django 版本:3.0.4 Python 版本:3.7.3 数据库:x86_64-pc-linux-gnu 上的 PostgreSQL 11.9 (Debian 11.9-0+deb10u1),由 gcc (Debian 8.3.0-6) 8.3 编译.0, 64 位

【问题讨论】:

    标签: django


    【解决方案1】:

    幸运的是;我找到了上述问题的解决方案,该解决方案生成了所需的确切 SQL 查询:

    t=pr.objects.filter(
    # Restrict the count query to records in outer query    
    Q(p =OuterRef('p')) | Q(c = OuterRef('p'))).
    #Anottate a static on which Count group by
    annotate(v=Value(1, IntegerField())).
    #Count can work only on a single field so restrict the output to v field only
    values('v').
    #Perform Count on v field with a wildcard so we get no unnecessary 'Group By' clause
    annotate(cntr=Count('*')).
    #Restrict output to one field because a sub query should return one field
    values('cntr')
    ro = pr.objects.filter(pk=7604).annotate(cntp=Subquery(t))
    

    生成的 SQL 查询是:

        SELECT "eftapp_pr"."id", "eftapp_pr"."p_id", "eftapp_pr"."r", "eftapp_pr"."c_id",
     "eftapp_pr"."rc", (SELECT COUNT(*) AS "cntr" FROM "eftapp_pr" U0 WHERE (U0."p_id" = 
    "eftapp_pr"."p_id" OR U0."c_id" = "eftapp_pr"."p_id")) AS "cntp" FROM "eftapp_pr" WHERE
     "eftapp_pr"."id" = 7604
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-02-24
      • 2019-08-03
      • 2018-06-15
      • 2020-05-23
      • 2017-07-21
      • 2023-03-29
      • 2019-12-30
      • 1970-01-01
      相关资源
      最近更新 更多