#17502: Filter on field from base class 2 levels up hierarchy causes extra join --------------------------------------+------------------------------------ Reporter: dbenamy@… | Owner: lrekucki Type: Cleanup/optimization | Status: assigned Component: Uncategorized | Version: 1.3 Severity: Normal | Resolution: Keywords: | Triage Stage: Accepted Has patch: 0 | Needs documentation: 0 Needs tests: 0 | Patch needs improvement: 0 Easy pickings: 0 | UI/UX: 0 --------------------------------------+------------------------------------ Changes (by lrekucki):
* status: new => assigned * stage: Unreviewed => Accepted Comment: Going step by step: 1. We start with table "C". 2. {{{Query.setup_inherited_model()}}} adds {{{INNER JOIN}}} for all parent models. C is joined with B [{{{ ON (C.b_ptr_id = B.a_ptr_id) - B's parent pointer is it's own primary key). Then C is joined with A and because of B's PK is the same as B's parent pointer, Django optimizes? by doing (C.b_ptr_id [= B.a_ptr_id] = A.id). 3. Then the {{{Query.add_filter()}}} lookups the {{{alice}}} field and setups neccesary joins with {{{Query.setup_joins()}}}. As the field resides on the base model, the following code is executed: {{{#!python for int_model in opts.get_base_chain(model): if int_model is proxied_model: opts = int_model._meta else: lhs_col = opts.parents[int_model].column dedupe = lhs_col in opts.duplicate_targets if dedupe: exclusions.update(self.dupe_avoidance.get( (id(opts), lhs_col), ())) dupe_set.add((opts, lhs_col)) opts = int_model._meta alias = self.join((alias, opts.db_table, lhs_col, opts.pk.column), exclusions=exclusions) joins.append(alias) exclusions.add(alias) for (dupe_opts, dupe_col) in dupe_set: self.update_dupe_avoidance(dupe_opts, dupe_col, alias) }}} This takes a different approach by joining C with B (alias lookup succeeds) and then B with A (alias lookup fails, because we previously used a different connection). This is where the additional join comes from. Possible solutions: a. make the strategy in 2. and 3. consistent. Chaining the joins step by step instead of skipping should work, but if we select only values from C and A, we should be able to skip joining B. This would suggest changing {{{setup_joins to}}} try to reuse the optimized alias first. b. When doing the {{{setup_inherited_models()}}}, if we did join to B (i.e. there are selected fields to B), add a fake entry to {{{alias_map}}} for "B INNER JOIN A", that {{{setup_joins()}}} could reuse later. -- Ticket URL: <https://code.djangoproject.com/ticket/17502#comment:2> 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 post to this group, send email to django-updates@googlegroups.com. To unsubscribe from this group, send email to django-updates+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/django-updates?hl=en.