#34262: Queryset grouped by annotation with aggregates on another annotated
expression crashes on MySQL with sql_mode=only_full_group_by.
-------------------------------------+-------------------------------------
     Reporter:  Mariusz Felisiak     |                    Owner:  ontowhee
         Type:  Bug                  |                   Status:  assigned
    Component:  Database layer       |                  Version:  4.1
  (models, ORM)                      |
     Severity:  Normal               |               Resolution:
     Keywords:  mysql                |             Triage Stage:  Accepted
  only_full_group_by                 |
    Has patch:  0                    |      Needs documentation:  0
  Needs tests:  0                    |  Patch needs improvement:  0
Easy pickings:  0                    |                    UI/UX:  0
-------------------------------------+-------------------------------------
Comment (by ontowhee):

 Thank you so much for the detailed clarifications! It helps me understand
 the context. The chain of thoughts for the user that you outlined makes a
 lot of sense, and it is what I would prefer in my own workflow, too.


 ----


 I have WIP in this
 [https://github.com/django/django/compare/main...ontowhee:django:ticket-34262
 branch]. I’ve added AnyValue, along with some tests.


 One thing I'm not sure about is, in MySQL, the expression
 `ANY_VALUE(LEAST(MIN(pages), GREATEST(pages, 600)))` is valid, but
 creating the expression in django with `AnyValue(Least("min_pages",
 "greatest_page"))` leads to the following error during the resolving
 phase,

 {{{
 Cannot compute AnyValue(’Least(F(min_pages), F(greatest_pages))’):
 ‘Least(F(min_pages), F(greatest_pages))’ is an aggregate
 }}}

 For Postgresql and Oracle, it makes sense to raise this error, because
 ANY_VALUE is considered an aggregate function, and nested aggregates are
 not allowed. However, for MySQL, would it make sense to not raise the
 error? Would that mean changing how
 [https://github.com/django/django/blob/main/django/db/models/aggregates.py#L81
 resolve_expression] is checking the `contains_aggregate` attribute? The
 resolving phase is backend agnostic. Trying to catch it in the compilation
 phase in `as_mysql()` would be too late, because the error would have
 already been raise. I don't have a clear path forward at this point, but
 will dig around and think about this more. Any ideas and suggestions are
 welcomed, of course!


 ----


 I'm also a bit stuck on understanding how to write the SQL statement for
 `ANY_VALUE(…) FILTER(…) OVER(…)`. I was running statements directly in
 MySQL to better understand the different combinations of the FILTER, OVER,
 GROUP BY clauses, trying add support according to the chart in the [https
 ://modern-sql.com/caniuse/any_value article], but I'm getting syntax
 errors.

 Here is a simple example I was running,

 {{{
 CREATE TABLE person(id int, first_name varchar(255), dob date);
 INSERT INTO person(id, first_name, dob)
 VALUES(1, 'alice', '2025-01-01'),
 (2, 'alice', '2024-02-02'),
 (3, 'alice', '2025-03-03'),
 (4, 'bob', '2025-04-04'),
 (5, 'charlie', '2025-05-05');
 }}}

 These statement worked in MySQL.

 {{{
 SELECT ANY_VALUE(id) FROM person;
 SELECT ANY_VALUE(id) FROM person GROUP BY first_name;
 }}}

 These statements did not work. They returned a syntax error. I expected
 the last one, `ANY_VALUE(..) FILTER(...) OVER(...)`, to be valid,
 according to the chart, but was surprised to see the syntax error. All the
 statements work in Postgresql.

 {{{
 SELECT ANY_VALUE(id) OVER() FROM person;
 SELECT ANY_VALUE(id) FILTER(WHERE id > 1) FROM person;
 SELECT ANY_VALUE(id) FILTER(WHERE id > 1) OVER() FROM person;
 SELECT ANY_VALUE(id) FILTER(WHERE id > 1) OVER(partition by first_name)
 FROM person;
 }}}


 MySQL does not support [https://modern-sql.com/feature/filter FILTER
 clause], and django will emulate using
 [https://github.com/django/django/blob/main/django/db/models/aggregates.py#L142
 CASE] instead. I wonder if the chart in the article is accurate, or if the
 FILTER clause in ANY_VALUE is a special case.

 If anyone can see what I'm doing wrong here, and give an example of a
 valid statement with `ANY_VALUE(..) FILTER(...) OVER(...)`, that would be
 helpful!
-- 
Ticket URL: <https://code.djangoproject.com/ticket/34262#comment:16>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/django-updates/01070194fd101f24-379fa6b5-07cc-4d1e-ba2d-c88034d26d9f-000000%40eu-central-1.amazonses.com.

Reply via email to