Opened 4 years ago
Last modified 13 months ago
#32277 new New feature
Support nested aggregates in window expressions.
Description ¶
When trying do something like
Sum(Count('field_name'))
Django raises a FieldError exception with the message Cannot compute Sum('count'): 'count' is an aggregate"
. That makes sense except when Sum(Count())
is the expression of a Window function, where nesting two aggregates is a valid SQL expression:
Window(expression=Sum(Count('field_name')), **omitted_window)
I may be missing some edge case here but would it be possible to check, in the django.db.aggregates.Aggregate Func resolve_expression method, whether the expression containing another aggregate is the main expression of a Window function and, in that case, allow the execution of the SQL? Or is there any way to circumvent this aggregation nesting limitation in window functions?
According to the ticket's flags, the next step(s) to move this issue forward are:
- To provide a patch by sending a pull request. Claim the ticket when you start working so that someone else doesn't duplicate effort. Before sending a pull request, review your work against the patch review checklist. Check the "Has patch" flag on the ticket after sending a pull request and include a link to the pull request in the ticket comment when making that update. The usual format is:
[https://github.com/django/django/pull/#### PR]
.
Change History (4)
comment:1 by , 4 years ago
Cc: | added |
---|---|
Resolution: | → needsinfo |
Status: | new → closed |
Summary: | Aggregate of aggregate not allowed in window functions → Support nested aggregates in window expressions. |
Type: | Bug → New feature |
comment:2 by , 4 years ago
Sure! Here's an example. Assuming the following models/tables:
users
| id | group | name | 1 A user1 2 A user2 3 B user3
comments
| id | user_id | comment | 1 1 hi 2 1 there 3 2 foo 4 3 bar
I am able to do an SQL query like the following in both PostgreSQL and Exasol (The DBs I currently have "at hand"):
SELECT u.group, u.name, count(1) as user_comments_count, sum(count(1)) OVER (PARTITION BY u.group) as group_comments_count FROM users u INNER JOIN comments c ON c.user_id = u.id GROUP BY u.group, u.name
With the following result:
| group | name | user_comments_count | group_comments_count | A user1 2 3 A user2 1 3 B user3 1 1
Therefore, I was expecting to be able to use the ORM like this (not tested, bear in mind I could be making mistakes here but I hope you get the idea, sorry!):
class User(models.Model): group = models.charField(max_length=2) name = models.charField(max_length=20, unique=True) class Comments(models.Model): user = models.ForeignKey(User, on_delete=models.SET_NULL) coment = models.charField(max_length=500) User.objects.annotate( user_comments_count=Count('comments__id'), group_comments_count=Window(expression=Sum(Count('comments__id')), partition_by=[F('group')]), ).values( 'id', 'group', 'user_comments_count', 'group_comments_count', )
comment:3 by , 4 years ago
Resolution: | needsinfo |
---|---|
Status: | closed → new |
Triage Stage: | Unreviewed → Accepted |
Version: | 3.0 → master |
Thanks for en example. It looks reasonable but an implementation can be tricky because all aggregations must be included in the GROUP BY
clause. Tentatively accepted, however it may not be worth complexity. I would use a subquery for user_comments_count
instead.
comment:4 by , 13 months ago
Cc: | added |
---|
I'm not sure about this, can you provide an example of useful window expression with nested aggregates. I've tried to remove this check for window expressions and it crashes on PostgreSQL:
and Oracle:
it works only on SQLite but the result is quite unexpected.