Opened 4 years ago
Last modified 10 months ago
#32277 new New feature
Support nested aggregates in window expressions.
Reported by: | Héctor Pablos | Owned by: | nobody |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | dev |
Severity: | Normal | Keywords: | window, aggregates |
Cc: | Mads Jensen, John Speno | Triage Stage: | Accepted |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
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?
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 , 10 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.