Ysr*_*nja 10 python django django-aggregation django-annotate django-subquery
请帮助我,我已经被困在这个问题上太久了:(
我有这两个模型:
class Specialization(models.Model):
name = models.CharField("name", max_length=64)
class Doctor(models.Model):
name = models.CharField("name", max_length=128)
# ...
specialization = models.ForeignKey(Specialization)
Run Code Online (Sandbox Code Playgroud)
我想用具有该专业的医生数量来注释查询集中的所有专业。
我经历了一个循环并做了一个简单的: Doctor.objects.filter(specialization=spec).count()
然而事实证明这太慢而且效率低下。我读得越多,就越意识到使用此处SubQuery
来筛选专业化的医生是有意义的OuterRef
。这就是我想出的:
doctors = Doctor.objects.all().filter(specialization=OuterRef("id")) \
.values("specialization_id") \
.order_by()
add_doctors_count = doctors.annotate(cnt=Count("specialization_id")).values("cnt")[:1]
spec_qs_with_counts = Specialization.objects.all().annotate(
num_applicable_doctors=Subquery(add_doctors_count, output_field=IntegerField())
)
Run Code Online (Sandbox Code Playgroud)
对于每个专业,我得到的输出仅为 1。代码只是用它来注释每个医生对象specialization_id
,然后注释该组内的计数,这意味着它将是 1。
不幸的是,这对我来说并不完全有意义。在我最初的尝试中,我使用了一个聚合来进行计数,虽然它可以单独工作,但它不能作为 a 工作SubQuery
,但我收到此错误:
This queryset contains a reference to an outer query and may only be used in a subquery.
我之前发布过这个问题,有人建议这样做Specialization.objects.annotate(count=Count("doctor"))
然而,这不起作用,因为我需要计算特定的医生查询集。
但是,我没有得到相同的结果:
https://docs.djangoproject.com/en/1.11/ref/models/expressions/
https://medium.com/@hansonkd/the-dramatic-benefits-of-django-subqueries-and-annotations-4195e0dafb16
如果您有任何问题可以使这一点更清楚,请告诉我。
Sla*_*ava 28
问题是 DjangoGROUP BY
一旦发现使用聚合函数就会添加。
因此,您可以创建自己的聚合函数,但 Django 认为它不是聚合函数。像这样:
doctors = Doctor.objects.filter(
specialization=OuterRef("id")
).order_by().annotate(
count=Func(F('id'), function='Count')
).values('count')
spec_qs_with_counts = Specialization.objects.annotate(
num_applicable_doctors=Subquery(doctors)
)
Run Code Online (Sandbox Code Playgroud)
有关此方法的更多详细信息,您可以在此答案中看到:/sf/answers/4831451271/
还可以在有关在子查询表达式和func 表达式中使用聚合的文档中找到有用的信息。
Wil*_*sem 11
Doctor
s Specialization
我认为你让事情变得过于复杂,可能是因为你认为这Count('doctor')
会计算每个专业的每个医生(无论该医生的专业是什么)。事实并非如此,如果你有Count
这样的相关对象,Django 会隐式地查找相关对象。事实上你Count('unrelated_model')
根本不能,只有通过像ForeignKey
、ManyToManyField
等关系(包括反向)才能查询这些,因为否则这些不是很有意义。
我想用具有该专业的医生数量来注释查询集中的所有专业。
您可以通过简单的操作来做到这一点:
# Counting all doctors per specialization (so not all doctors in general)
from django.db.models import Count
Specialization.objects.annotate(
num_doctors=Count('doctor')
)
Run Code Online (Sandbox Code Playgroud)
现在,此Specialization
查询集中的每个对象都将具有一个额外的属性,该属性是一个整数(具有该专业的医生的数量)。num_doctors
您还可以在同一查询中对Specialization
s 进行过滤(例如,仅获取以 结尾的专业化'my'
)。只要您不过滤相关的doctor
集合,Count
就会起作用(请参阅下面的部分如何执行此操作)。
然而,如果您过滤相关的doctor
,那么相关的计数将过滤掉这些医生。此外,如果您过滤另一个相关对象,那么这将产生一个额外的JOIN
,它将充当s的乘数Count
。在这种情况下,最好使用它num_doctors=Count('doctor', distinct=True)
。您始终可以使用distinct=True
(无论您是否执行额外的JOIN
操作),但它会对性能产生很小的影响。
上面的代码之所以有效,是因为Count('doctor')
它不是简单地将所有医生添加到查询中,而是LEFT OUTER JOIN
在doctor
s 表上创建一个,从而检查该specialization_id
表Doctor
是否正是我们要查找的医生。因此 Django 将构造的查询如下所示:
SELECT specialization.*
COUNT(doctor.id) AS num_doctors
FROM specialization
LEFT OUTER JOIN doctor ON doctor.specialization_id = specialization.id
GROUP BY specialization.id
Run Code Online (Sandbox Code Playgroud)
对子查询执行相同的操作将在功能上得到相同的结果,但如果 Django ORM 和数据库管理系统没有找到优化此方法的方法,这可能会导致昂贵的查询,因为对于每个专业化,它都可能导致数据库中的额外子查询。
Doctor
数Specialization
但是,假设您只想计算姓名以Joe开头的医生,那么您可以在相关的上添加过滤器doctor
,例如:
# counting all Doctors with as name Joe per specialization
from django.db.models import Count
Specialization.objects.filter(
doctor__name__startswith='Joe' # sample filter
).annotate(
num_doctors=Count('doctor')
)
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
23842 次 |
最近记录: |