如何在Django ORM中实现多左连接并统计关联表记录数
Django ORM实现Companies表与子表左连接并统计关联次数
嘿,我来帮你把这条原生PostgreSQL查询转换成Django ORM的写法,完美匹配你的需求~
首先,咱们先对齐思路:原生SQL里是先对两张子表做分组计数,再左连接到Companies表,保留所有公司记录,没有关联记录的计数返回null。用Django ORM的话,我们可以用Subquery和OuterRef来复刻这个子查询逻辑,避免多表连接带来的计数误差。
具体实现代码
首先导入需要的模块:
from django.db.models import Count, Subquery, OuterRef from django.db import models
然后构建两个子查询,分别计算每个公司在两张子表中的出现次数:
# 构建OrPodanieIssues的计数子查询 or_podanie_count_subquery = OrPodanieIssues.objects.filter( company_id=OuterRef('cin') # 关联Companies的cin字段 ).values('company_id').annotate( count=Count('id') # 统计当前company_id的记录数 ).values('count') # 只提取计数值 # 构建ZnizenieImaniaIssues的计数子查询 znizenie_count_subquery = ZnizenieImaniaIssues.objects.filter( company_id=OuterRef('cin') ).values('company_id').annotate( count=Count('id') ).values('count')
最后查询Companies表,同时把两个计数字段添加到结果中:
companies_with_counts = Companies.objects.annotate( # 把子查询的结果作为新字段添加,指定输出类型为IntegerField or_podanie_issues_count=Subquery(or_podanie_count_subquery, output_field=models.IntegerField()), znizenie_imania_issues_count=Subquery(znizenie_count_subquery, output_field=models.IntegerField()) ).values( # 指定需要返回的字段,和原生SQL输出一致 'cin', 'name', 'br_section', 'adress_line', 'last_update', 'or_podanie_issues_count', 'znizenie_imania_issues_count' )
效果说明
- 这段ORM代码生成的SQL和你提供的原生查询几乎一致,保证执行效率和结果准确性
- 如果某个公司在子表中没有关联记录,对应的计数字段会返回
None,和你示例中的null效果完全匹配 - 遍历
companies_with_counts就能得到你想要的结果格式,比如:
for company in companies_with_counts: print(company)
输出示例:
{ 'cin': 9110007690010, 'name': "Alžbeta Škrováňová", 'br_section': None, 'adress_line': "Za Humnami, 95161 Čifáre", 'last_update': datetime.datetime(2018, 9, 19, 3, 0, 32, 851000), 'or_podanie_issues_count': 3, 'znizenie_imania_issues_count': None }
为什么不用直接annotate Count?
如果直接用Companies.objects.annotate(or_count=Count('orpodanieissues'))的方式,当一个公司同时有多个OrPodanieIssues和ZnizenieImaniaIssues记录时,会因为多表连接产生笛卡尔积,导致计数结果不准确。而用子查询的方式完全复刻了原生SQL的逻辑,能避免这个问题。
内容的提问来源于stack exchange,提问作者Chiller
相关产品推荐
相关产品推荐

