Django + PostgreSQL: Fill missing dates in a range

老子叫甜甜 提交于 2021-01-27 05:30:56

问题


I have a table with one of the columns as date. It can have multiple entries for each date.

date         .....
-----------  -----
2015-07-20     ..
2015-07-20     ..
2015-07-23     ..
2015-07-24     ..

I would like to get data in the following form using Django ORM with PostgreSQL as database backend:

date         count(date)
-----------  -----------
2015-07-20        2
2015-07-21        0       (missing after aggregation)
2015-07-22        0       (missing after aggregation)
2015-07-23        1
2015-07-24        1

Corresponding PostgreSQL Query:

WITH RECURSIVE date_view(start_date, end_date) 
AS ( VALUES ('2015-07-20'::date, '2015-07-24'::date) 
     UNION ALL SELECT start_date::date + 1, end_date 
     FROM date_view 
     WHERE start_date < end_date ) 
SELECT start_date, count(date) 
FROM date_view LEFT JOIN my_table ON date=start_date 
GROUP BY date, start_date 
ORDER BY start_date ASC;

I'm having trouble translating this raw query to Django ORM query.

It would be great if someone can give a sample ORM query with/without a workaround for Common Table Expressions using PostgreSQL as database backend.

The simple reason is quoted here:

My preference is to do as much data processing in the database, short of really involved presentation stuff. I don't envy doing this in application code, just as long as it's one trip to the database

As per this answer django doesn't support CTE's natively, but the answer seems quite outdated.

References:

  • MySQL: Select All Dates In a Range Even If No Records Present

  • WITH Queries (Common Table Expressions)

Thanks


回答1:


I do not think you can do this with pure Django ORM, and I am not even sure if this can be done neatly with extra(). The Django ORM is incredibly good in handling the usual stuff, but for more complex SQL statements and requirements, more so with DBMS specific implementations, it is just not quite there yet. You might have to go lower and down to executing raw SQL directly, or offload that requirement to be done by the application layer.

You can always generate the missing dates using Python, but that will be incredibly slow if the range and number of elements are huge. If this is being requested by AJAX for other use (e.g. charting), then you can offload that to Javascript.




回答2:


In stead of the recursive CTE you could use generate_series() to construct a calendar-table:

SELECT calendar, count(mt.zdate) as THE_COUNT
FROM generate_series('2015-07-20'::date
                   , '2015-07-24'::date
                   , '1 day'::interval)  calendar
LEFT JOIN my_table mt ON mt.zdate = calendar
GROUP BY 1
ORDER BY 1 ASC;

BTW: I renamed date to zdate. DATE is a bad name for a column (it is the name for a data type)



来源:https://stackoverflow.com/questions/31682737/django-postgresql-fill-missing-dates-in-a-range

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!