#24431: Combining extra(), annotate() or distinct() and count() can generate
invalid SQL
-------------------------------------+-------------------------------------
     Reporter:  briankrznarich       |                    Owner:  nobody
         Type:  Bug                  |                   Status:  new
    Component:  Database layer       |                  Version:  1.8beta1
  (models, ORM)                      |
     Severity:  Release blocker      |               Resolution:
     Keywords:  extra distinct       |             Triage Stage:  Accepted
  aggregate count annotate           |
    Has patch:  0                    |      Needs documentation:  0
  Needs tests:  0                    |  Patch needs improvement:  0
Easy pickings:  1                    |                    UI/UX:  0
-------------------------------------+-------------------------------------

Comment (by briankrznarich):

 Our specific need is ordering on a CASE statement.   As an example, for
 any employee in an organizational hierarchy we wanted to show all of that
 employee's subordinates first, followed by all of his superiors.   The
 subordinates are ordered top-down, the superiors are ordered bottom-up.

 I was not aware of F() functions, those will be useful.  Since you pointed
 it out, I've started perusing the new conditional-expression
 documentation, and it looks like we might be able to replace extra() with
 that.
 https://docs.djangoproject.com/en/1.8/ref/models/conditional-expressions/

 extra()'s `tables=` argument doesn't work unless you also provide a
 `where=` clause where you do the join yourself.  Otherwise the table just
 gets tacked on after a comma. (`SELECT * FROM
 auth_permission,django_content_type WHERE....`).   This only happens,
 however, if the table wasn't already needed for a JOIN(which itself seems
 contrary to the documentation, but anyway...).

 As a consequence, and a final example of a stupid Django trick, I've
 combined all of these misbehaviors into one fun queryset.  You'd think
 len(queryset) and queryset.count() would yield the same value, but....

 {{{#!python
 In [46]: q =
 
Permission.objects.all().select_related('content_type').extra(tables=['django_content_type'],select={'foo':"django_content_type.app_label"})

 In [47]: q.count()
 Out[47]: 2188688

 In [48]: len(q)
 Out[48]: 2581

 In [49]: q.count()
 Out[49]: 2581

 }}}
 The call to len() caches the queryset, and permanently alters the return
 value of count().   The database counts show what is going on...
 {{{
 => select count(*) from auth_permission;
  count
 -------
   2581
 (1 row)

 => select count(*) from django_content_type;
  count
 -------
    848
 (1 row)

 => select count(*) from django_content_type,auth_permission;
   count
 ---------
  2188688
 }}}

 Anyway, SQL generation is hard, but Django does an incredible job of it.
 You just can't win 'em all.

 Thanks for your help.  As I said above, now the I know it exists, the
 extra() problem is easy to work around.  We'll certainly try to move to
 F() and friends after 1.8 is in production.

--
Ticket URL: <https://code.djangoproject.com/ticket/24431#comment:7>
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 post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/072.a75e05b3aecb666cfb0f3e598a8c019b%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to