#9342: query optimization issue
------------------------------------------+---------------------------------
          Reporter:  [EMAIL PROTECTED]  |         Owner:  nobody
            Status:  new                  |     Milestone:        
         Component:  Uncategorized        |       Version:  1.0   
        Resolution:                       |      Keywords:        
             Stage:  Unreviewed           |     Has_patch:  0     
        Needs_docs:  0                    |   Needs_tests:  0     
Needs_better_patch:  0                    |  
------------------------------------------+---------------------------------
Changes (by anonymous):

  * needs_better_patch:  => 0
  * needs_tests:  => 0
  * needs_docs:  => 0

Comment:

 After looking at this more, I think the fault possibly lays in
 Query::get_from_clause (...\django\db\models\sql\query.py).

 The code iterates over an array of joins for known alias fields; checking
 whether its internal counter indicates that the alias is referenced or
 not. If the number of references is zero, it just skips that join. The
 problem is that that alias is possibly referenced in another join; but
 that is not being checked.

 I added a check and if such a case exists, the internal reference counter
 is increased for such an alias. I'm not sure if this is the  correct patch
 but it is a start:
 {{{
     def get_from_clause(self):
         """
         Returns a list of strings that are joined together to go after the
         "FROM" part of the query, as well as a list any extra parameters
 that
         need to be included. Sub-classes, can override this to create a
         from-clause via a "select", for example (e.g. CountQuery).

         This should only be called after any SQL construction methods that
         might change the tables we need. This means the select columns and
         ordering must be done first.
         """
         result = []
         qn = self.quote_name_unless_alias
         qn2 = self.connection.ops.quote_name
         first = True
         for alias in self.tables:
             if not self.alias_refcount[alias]:
                 #START HACK: No sure if it is a problem with
 self.alias_refcount somewhere else or this code.
                 #      Need to iterate over self.alias_map to ensure that
 another alias map does not
                 #      reference this alias.
                 for alias_check in self.tables:
                     if alias != alias_check:
                         try:
                             name, rhs, join_type, lhs, lhs_col, col,
 nullable = self.alias_map[alias_check]
                         except KeyError:
                             continue
                         if alias in [rhs,lhs]:
                             self.ref_alias(alias)
                 #END HACK

             if not self.alias_refcount[alias]:
                 continue
             try:
                 name, alias, join_type, lhs, lhs_col, col, nullable =
 self.alias_map[alias]
             except KeyError:
                 # Extra tables can end up in self.tables, but not in the
                 # alias_map if they aren't in a join. That's OK. We skip
 them.
                 continue
             alias_str = (alias != name and ' %s' % alias or '')
             if join_type and not first:
                 result.append('%s %s%s ON (%s.%s = %s.%s)'
                         % (join_type, qn(name), alias_str, qn(lhs),
                            qn2(lhs_col), qn(alias), qn2(col)))
             else:
                 connector = not first and ', ' or ''
                 result.append('%s%s%s' % (connector, qn(name), alias_str))
             first = False
         for t in self.extra_tables:
             alias, unused = self.table_alias(t)
             # Only add the alias if it's not already present (the
 table_alias()
             # calls increments the refcount, so an alias refcount of one
 means
             # this is the only reference.
             if alias not in self.alias_map or self.alias_refcount[alias]
 == 1:
                 connector = not first and ', ' or ''
                 result.append('%s%s' % (connector, qn(alias)))
                 first = False
         return result, []

 }}}

 One thing to note about this initial patch attempt; the SQL is *correct*
 but joins the missing table twice, once in the where clause and once in
 the sub-select. Here is what the SQL would look like:
 {{{
 SELECT "tableB"."key_id"
 FROM "tableB" INNER JOIN "tableA" ON ("tableB"."key_id" =
 "tableA"."key_id")
      WHERE NOT ("tableB"."key_id" IN (SELECT U2."key_id" FROM "tableB" U0
                                                           INNER JOIN
 "tableA" U1 ON (U1."key_id" = U0."key_id")
                                                           INNER JOIN
 "tableC" U2 ON (U1."key_id" = U2."key_id") WHERE UPPER(U2."data"::text) =
 UPPER('test') ))

 }}}

-- 
Ticket URL: <http://code.djangoproject.com/ticket/9342#comment:1>
Django <http://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 post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/django-updates?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to