Ticket #9870: aggregation-m2m-opt.3.diff

File aggregation-m2m-opt.3.diff, 2.8 KB (added by Alex Gaynor, 15 years ago)

added a comment describing the optimization and a test to show that it works, the test is slightly fragile but should be ok

  • django/db/models/sql/query.py

    diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
    index ed21ea7..3c06376 100644
    a b class BaseQuery(object):  
    11941194
    11951195        elif (len(field_list) > 1 or
    11961196            field_list[0] not in [i.name for i in opts.fields]):
     1197               
     1198            join_alias = self.get_initial_alias()
     1199           
    11971200            field, target, opts, join_list, last, _ = self.setup_joins(
    1198                 field_list, opts, self.get_initial_alias(), False)
     1201                field_list, opts, join_alias, False)
    11991202            # Aggregate references a model or field that requires a join
    12001203            self.allow_nulls = True
    1201 
    1202             col = (join_list[-1], target.column)
     1204           
     1205            # here we preform an optimization(the same one as done in add_filter)
     1206            # if the last table we join to we are joining to the same column as
     1207            # the one we are coming from we don't need to do the second join,
     1208            # which means for an m2m we only join the intermediary table, and not
     1209            # the final table.  This cuts the joins in half for a query like:
     1210            # Model.objects.annotate(Count('m2m'))
     1211            final = len(join_list)
     1212            penultimate = last.pop()
     1213            if penultimate == final:
     1214                penultimate = last.pop()
     1215            join_alias = join_list[-1]
     1216           
     1217            col = target.column
     1218           
     1219            while final > 1:
     1220                join = self.alias_map[join_alias]
     1221                if col != join[RHS_JOIN_COL]:
     1222                    break
     1223                self.unref_alias(join_alias)
     1224                join_alias = join[LHS_ALIAS]
     1225                col = join[LHS_JOIN_COL]
     1226                join_list = join_list[:-1]
     1227                final -= 1
     1228                if final == penultimate:
     1229                    penultimate = last.pop()
     1230           
     1231            col = (join_list[-1], col)
    12031232            aggregate = aggregate_expr.add_to_query(self, aggregates,
    12041233                col=col,
    12051234                source=target,
  • tests/regressiontests/aggregation_regress/models.py

    diff --git a/tests/regressiontests/aggregation_regress/models.py b/tests/regressiontests/aggregation_regress/models.py
    index ea0acc8..c36398c 100644
    a b FieldError: Cannot resolve keyword 'foo' into field. Choices are: authors, id, i  
    135135>>> Publisher.objects.annotate(avg_price=Avg('book__price')).aggregate(Max('avg_price'))
    136136{'avg_price__max': 75.0...}
    137137
     138# Verify that we only join to the intermediary table, instead of having a second
     139# join to get to the second table since it isn't necessary.
     140>>> Book.objects.annotate(Count('authors')).query.as_sql()[0].count('JOIN')
     1411
    138142"""
    139143}
    140144
Back to Top