如何用Django Queryset实现每月首个记录日期查询?
获取每月首个记录日期的Django Queryset实现方法
需求说明
获取每月的首个记录日期(不一定是当月1号),用于图表标签。
MySQL原生实现
直接用SQL可以实现需求:
-- 查询每月首个记录日期 SELECT MIN(recorded_date) AS recorded_date FROM daily_table GROUP BY DATE_FORMAT(`recorded_date`, "%Y-%m");
示例结果:
| recorded_date |
|---|
| 2022-09-05 |
| 2022-10-03 |
| 2022-11-01 |
| 2022-12-01 |
| 2023-01-03 |
错误的Django Queryset尝试
之前的代码因分组逻辑错误,返回了当月多条记录:
first_days = Industry.objects\ .values('recorded_date')\ .annotate(ym=Func(F('recorded_date'), Value('%Y%m'), function='DATE_FORMAT', output_field=CharField()))\ .annotate(count=Count('id'))
对应的SQL:
SELECT `vietnam_research_industry`.`recorded_date`, DATE_FORMAT(`vietnam_research_industry`.`recorded_date`, '%Y%m') AS `ym`, COUNT(`vietnam_research_industry`.`id`) AS `count` FROM `vietnam_research_industry` GROUP BY `vietnam_research_industry`.`recorded_date`, DATE_FORMAT(`vietnam_research_industry`.`recorded_date`, '%Y%m') ORDER BY NULL
错误结果(当月多条重复记录):
| recorded_date | ym | count |
|---|---|---|
| 2022-12-16 | 202212 | 757 |
| 2022-12-21 | 202212 | 757 |
| 2022-12-22 | 202212 | 757 |
| 2022-12-23 | 202212 | 757 |
| 2022-12-26 | 202212 | 757 |
| 2022-12-27 | 202212 | 757 |
| 2022-12-28 | 202212 | 757 |
| 2022-12-29 | 202212 | 757 |
| 2022-12-30 | 202212 | 757 |
正确的Django Queryset实现
不需要使用raw方法,调整分组和聚合逻辑即可实现需求:
from django.db.models import Func, F, Value, Min, Count from django.db.models.fields import CharField # 仅获取每月首个记录日期 first_days = Industry.objects.annotate( ym=Func(F('recorded_date'), Value('%Y-%m'), function='DATE_FORMAT', output_field=CharField()) ).values('ym').annotate( first_recorded_date=Min('recorded_date') ).values('first_recorded_date') # 如需同时统计每月记录总数 first_days_with_count = Industry.objects.annotate( ym=Func(F('recorded_date'), Value('%Y-%m'), function='DATE_FORMAT', output_field=CharField()) ).values('ym').annotate( first_recorded_date=Min('recorded_date'), total_count=Count('id') ).values('first_recorded_date', 'total_count')
逻辑说明
- 先为每条记录标注对应的年月标识
ym - 按
ym分组,每组取最小的recorded_date(即当月首个记录日期) - 按需提取目标字段,最终结果与原生SQL的预期输出一致
内容的提问来源于stack exchange,提问作者yoshitaka okada
相关产品推荐
相关产品推荐

