【发布时间】:2020-06-19 13:04:27
【问题描述】:
问题概述
给定模型
class Candidate(BaseModel):
name = models.CharField(max_length=128)
class Status(BaseModel):
name = models.CharField(max_length=128)
class StatusChange(BaseModel):
candidate = models.ForeignKey("Candidate", related_name="status_changes")
status = models.ForeignKey("Status", related_name="status_changes")
created_at = models.DateTimeField(auto_now_add=True, blank=True)
和 SQL 表:
candidates
+----+--------------+
| id | name |
+----+--------------+
| 1 | Beth |
| 2 | Mark |
| 3 | Mike |
| 4 | Ryan |
+----+--------------+
status
+----+--------------+
| id | name |
+----+--------------+
| 1 | Review |
| 2 | Accepted |
| 3 | Rejected |
+----+--------------+
status_change
+----+--------------+-----------+------------+
| id | candidate_id | status_id | created_at |
+----+--------------+-----------+------------+
| 1 | 1 | 1 | 03-01-2019 |
| 2 | 1 | 2 | 05-01-2019 |
| 4 | 2 | 1 | 01-01-2019 |
| 5 | 3 | 1 | 01-01-2019 |
| 6 | 4 | 3 | 01-01-2019 |
+----+--------------+-----------+------------+
我想获取具有给定状态的候选人总数,但只计算最新的status_change。
换句话说,StatusChange 用于跟踪状态的历史记录,但在计算候选人的当前状态时只考虑最新的。
SQL 解决方案
使用 SQL,我能够使用 Group BY 和 COUNT 实现它。 (未经测试的 SQL)
SELECT
status.id as status_id
, status.name as status_name
, COUNT(*) as status_count
FROM
(
SELECT
status_id,
Max(created_at) AS latest_status_change
FROM
status_change
GROUP BY status_id
)
AS last_status_count
INNER JOIN
last_status_count AS status
ON (last_status_count.status_id = status.id)
GROUP BY status.name
ORDER BY status_count DESC;
last_status_count
+-----------+-------------+--------+
| status_id | status_name | count |
+-----------+-------------+--------+
| 1 | Review | 2 | # <= Does not include instance from candidate 1
| 2 | Accepted | 1 | # because status 2 is latest
| 3 | Rejected | 1 |
+-----------+-------------+--------+
尝试过的 Django 解决方案
我需要一个视图来返回每个状态及其相应的计数 -
例如[{ status_name: "Review", count: 2 }, ...]
我不确定如何构建这个查询集,而不是在 python 中提取所有记录和聚合。
我认为我需要 annotate() 和可能的 Subquery,但我无法将它们拼接在一起。
我得到的最接近的是这个,它计算每个状态的状态更改次数,但计算非最新更改。
queryset = Status.objects.all().annotate(case_count=Count("status_changes"))
我发现了很多关于聚合的 SO 问题,但我找不到关于聚合和注释“最新”的明确答案。
提前致谢。
【问题讨论】:
-
同一个
candidate有可能有两个(或多个StatusChanges 和同一个create_id)? -
好问题。理论上是的,但是完全匹配是不可能的,因为它们带有时间戳(日期时间)。我也可以使用
idcol 来“排序”并定义最新的而不是created_at。 (PS:抱歉打错了,栏目应该是created_at)
标签: python mysql django django-queryset django-orm