#27867: Excluding a reverse relation produces inconsistent queries
-------------------------------------+-------------------------------------
               Reporter:  Sébastien  |          Owner:  nobody
  Diemer                             |
                   Type:  Bug        |         Status:  new
              Component:  Database   |        Version:  master
  layer (models, ORM)                |
               Severity:  Normal     |       Keywords:
           Triage Stage:             |      Has patch:  0
  Unreviewed                         |
    Needs documentation:  0          |    Needs tests:  0
Patch needs improvement:  0          |  Easy pickings:  0
                  UI/UX:  0          |
-------------------------------------+-------------------------------------
 When excluding a reverse relation from a queryset, the query generated by
 the ORM depends on the order in which the `exclude` kwargs are processed.
 This order is random since python 3.3 (see
 https://docs.python.org/3/reference/datamodel.html#object.__hash__).

 Django converts a query like
 `Tag.objects.filter(category_id=2).exclude(note__isnull=True)` to the
 following SQL:

 {{{
  SELECT "queries_tag"."id", "queries_tag"."name",
 "queries_tag"."parent_id", "queries_tag"."category_id"
  FROM "queries_tag"
  WHERE (
      "queries_tag"."category_id" = 2
      AND NOT (
          "queries_tag"."id" IN (
              SELECT U0."id" AS Col1 FROM "queries_tag" U0 LEFT OUTER JOIN
 "queries_note" U1 ON ( U0."id" = U1."tag_id" ) WHERE U1."id" IS NULL))
  )
  ORDER BY "queries_tag"."name" ASC
 }}}

 If another kwarg is added to `exclude`, then the subquery
 `"queries_tag"."id" IN` may be modified : a new clause is added  to the
 `WHERE` filter to avoid searching inside the entire `queries_tag` table.
 `WHERE U1."id" IS NULL` becomes `WHERE (U1."id" IS NULL AND U0."id" =
 ("queries_tag"."id"))`.
 This is not systematic, and seems to depend on the order on which the
 kwargs are processed (which is random for python3.3 and above).
 **This random behavior is the first thing to fix in my opinion.**

 In addition if the reverse relation is the only kwarg (like in my example
 above), the generated subquery misses this additional clause that enables
 to dramatically increase the performance of the query on large datasets.
 For performance reasons I think that the `WHERE` filtering in the subquery
 should always be `WHERE (U1."id" IS NULL AND U0."id" =
 ("queries_tag"."id"))`.
 **This is a second aspect of the bug that could be fixed.**

 see
 
https://github.com/sebdiem/django/commit/e8ca265e8c30f61c88ea17b7c1cab72695d6a4ff
 for details on how to reproduce the error.

--
Ticket URL: <https://code.djangoproject.com/ticket/27867>
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/050.b92e6ec5019cd61e0658dba3e723365a%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to