4

请帮助我,我已经坚持了太久了:(

我想做的事:

我有这两个模型:

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)

我想用具有该专业的医生数量来注释查询集中的所有专业。

到目前为止我的解决方案:

我经历了一个循环,我做了一个简单的: 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())
)

对于每个专业,我得到的输出只有 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"))

但是,这不起作用,因为我需要计算特定的医生查询集。

我已经关注了这些链接

但是,我没有得到相同的结果:

如果您有任何问题可以更清楚地说明这一点,请告诉我。

4

2 回答 2

8

计数所有 Doctors _ Specialization

我认为您使事情变得过于复杂,可能是因为您认为这Count('doctor')将计算每个专业的每个医生(无论该医生的专业如何)。它不会,如果你有Count这样的相关对象,Django 会隐式查找相关对象。事实上,你根本不能Count('unrelated_model'),只有通过关系(包括反向),如 a ForeignKeyManyToManyField等,你才能查询这些,否则这些不是很有意义

我想用具有该专业的医生数量来注释查询集中的所有专业。

你可以用一个简单的方法做到这一点:

#  Counting all doctors per specialization (so not all doctors in general)

from django.db.models import Count

Specialization.objects.annotate(
    num_doctors=Count('doctor')
)

现在,Specialization查询集中的每个对象都将具有一个额外的整数属性(具有该专业的医生的数量)。num_doctors

您还可以过滤Specialization同一查询中的 s(例如,仅获取以 结尾的特化'my')。只要您不过滤相关doctor的 s 集,它Count就会起作用(请参阅下面的部分如何执行此操作)。

但是,如果您过滤相关doctors,则相关计数将过滤掉这些医生。此外,如果您过滤另一个相关对象,那么这将产生一个额外的JOIN,它将充当s的乘数Count在这种情况下,使用它可能会更好num_doctors=Count('doctor', distinct=True)。你总是可以使用distinct=True(不管你是否做额外JOIN的s),但它会对性能产生很小的影响。

上面的方法之所以有效,是因为Count('doctor')它不会简单地将所有医生添加到查询中,它会LEFT OUTER JOINdoctors 表上创建一个 a,从而检查那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

对子查询做同样的事情在功能上会得到相同的结果,但是如果 Django ORM 和数据库管理系统没有找到优化它的方法,这可能会导致昂贵的查询,因为对于每个专业化,它都会导致数据库中的额外子查询。

计数特定 Doctor的 s perSpecialization

假设您只想计算名称以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')
)
于 2018-08-26T15:31:21.707 回答
3

问题

问题是 DjangoGROUP BY一看到就使用聚合函数添加。

解决方案

因此,您可以创建自己的聚合函数,但让 Django 认为它不是聚合函数。像这样:

doctors = Doctor.objects.filter(
    specialization=OuterRef("id")
).order_by().annotate(
    count=Func('id', 'Count')
).values('count')

spec_qs_with_counts = Specialization.objects.annotate(
    num_applicable_doctors=Subquery(doctors)
)

您可以在此答案中看到有关此方法的更多详细信息:https ://stackoverflow.com/a/69020732/10567223

于 2021-09-02T13:15:03.610 回答