在 django 中使用过滤计数而不是连接表返回错误值

Posted

技术标签:

【中文标题】在 django 中使用过滤计数而不是连接表返回错误值【英文标题】:using Filtered Count in django over joined tables returns wrong values 【发布时间】:2018-07-17 12:41:56 【问题描述】:

为了简单起见,我有四个表(A、B、Category 和 Relation),Relation 表存储 A 在 B 中的强度,Category 存储 B 的类型。

A <--- Relation ---> B ---> Category

(所以A和B的关系是n比n,其中B和Category的关系是n比1)

我需要的是计算A在类别中的出现率,使用:

A.objects.values(
    'id', 'relation_set__B__Category_id'
).annotate(
    ANum = Count('id', distinct=False)
)

请注意,如果我使用 'distinct=True' 而不是每个 'Anum' 将等于 1,这不是预期的结果。问题是我必须根据 B 发生的日期(以及 B 表中的一些其他字段)过滤计算, 我正在使用 django 2.0 的功能,这使得using filter as an argument in aggregation 成为可能。 假设:

kwargs= 
kwargs['relation_set__B____BDate__gte'] = the_start_limit

我可以在我的代码中使用它:

A.objects.values(
    'id', 'relation_set__B__Category_id'
).annotate(
    Anum = Count('id', distinct=False, filter=Q(**kwargs))
)

但是,由于表连接,我得到的结果是重复的,我不能像我解释的那样使用 distinct=True。 (查询 A 也是必须的,因为我必须聚合该表上的一些其他字段,如我的 question here 中所述)

我正在使用 Postgres 和 django 2.0.1。

是否有任何变通方法可以实现我的想法?

更新

使用另一个子查询完成:

# subquery
annotation = 
    'ANum': Count('relation_set__A_id', distinct=False, 
    filter=Q(**Bkwargs),

sub_filter = Q(relation_set__A_id=OuterRef('id')) & 
Q(Category_id=OuterRef('relation_set__B__Category_id'))
# you could annotate 'relation_set__B__Category_id' to A query an set the field here.
subquery = B.objects.filter(
    sub_filter
).values(
    'relation_set__A_id'
).annotate(**annotation).values('ANum')[:1]

# main query
A.objects.values(
    'id', 'relation_set__B__Category_id'
).annotate(
    Anum = Subquery(subquery)
)

【问题讨论】:

你写你需要“A在Category中的出现率”。我知道您想要一个特定类别对象的列表(由B.date 过滤),每个都用Anum 属性注释。不是您的查询创建的具有Anumattribute 的 A 对象列表。你能澄清一下吗? 当然。我需要一个按其“id”分组的 A 对象列表和带有注释字段 (Anum) 的类别表,该字段表示某个类别中 A 中 B 的出现次数。例如,在某个 Route-Part (Category) 中的事故 (B) 中,风险 (A) 高(强度)。如果我想知道某个 Route-Part 中某个风险的概率,我需要计算该 Route-Part 上发生的事故中风险的发生次数,然后将其除以该 Route-Part 中的事故总数。 @ascripter 【参考方案1】:

我仍然不确定我是否明白你想要什么。你写

请注意,如果我使用 'distinct=True' 而不是每个 'Anum' 将等于 1

当然。您将关联的 A 对象计数到每个 A 对象。每个都算自己。所以我仍然认为你不想用Anum注释A-objects,但可能是Categories。这个应该给你每个类别中所需的 As 数量。

Category.objects.annotate(
    Anum=Count(
        'b__relation__a',
        filter=Q(b__BDate__gte=the_start_limit),
        distinct=True
    )
)

'b__relation__a' 跟在relations backwards 之后,并选取与该类别相关的所有 A 对象。然而,过滤器将计数关系限制为某些 B。需要distinct=True 以避免query bug。

如果您真的想要“按其 id 分组的 A 对象列表”(而不仅仅是聚合的 Anum-count),正如您在评论中所述,我看不到一个简单的方法来做到这一点一个查询。

【讨论】:

请注意,如果没有使用 distinct=False 的过滤器参数,我会得到想要的结果,我需要过滤我的输入数据,这些数据正在馈送到计算中。我已经应用了您使用 Subquery() 提出的方法,但没有任何运气。将使用代码更新问题。 好的。我从 B 表开始使用 distinct=False 完成它。将用这种方法更新我的问题。 我已经更新了这个问题。然而,过滤问题仍然存在,我在new question 中询问过。

以上是关于在 django 中使用过滤计数而不是连接表返回错误值的主要内容,如果未能解决你的问题,请参考以下文章

Django Rest Framework 分页极慢计数

如何过滤 Django 查询的连接表,然后在一个查询中迭代连接表?

如何连接 Excel 数据透视表中的值,而不是求和或计数

Django 在模板标签中过滤和计数

COUNT(*) 在配置单元中返回多行而不是一

尝试在 Django 中的表单上使用脆表单过滤器时收到“无效表单:脆”错误,但仅在一个 django 应用程序中而不是另一个?