diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2011-03-09 19:17:45 -0500 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2011-03-09 19:17:45 -0500 |
| commit | e7be5a5729cabc6133b4fc83c06f6c5277a7af19 (patch) | |
| tree | acca1e4ff7c5683cbd4ae615bd05fc90062c5b2f /lib | |
| parent | a1288bf78f887aeef890e4e6cc0f769f4d6a81f5 (diff) | |
| download | sqlalchemy-e7be5a5729cabc6133b4fc83c06f6c5277a7af19.tar.gz | |
- Changed the underlying approach to query.count().
query.count() is now in all cases exactly:
query.
from_self(func.count(literal_column('1'))).
scalar()
That is, "select count(1) from (<full query>)".
This produces a subquery in all cases, but
vastly simplifies all the guessing count()
tried to do previously, which would still
fail in many scenarios particularly when
joined table inheritance and other joins
were involved. If the subquery produced
for an otherwise very simple count is really
an issue, use query(func.count()) as an
optimization. [ticket:2093]
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/sqlalchemy/orm/query.py | 107 |
1 files changed, 38 insertions, 69 deletions
diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index 43699b4d9..6f79d7c9c 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -435,7 +435,7 @@ class Query(object): this is passed through to :meth:`.FromClause.alias`. If ``None``, a name will be deterministically generated at compile time. - + """ return self.enable_eagerloads(False).statement.alias(name=name) @@ -1080,14 +1080,14 @@ class Query(object): SELECT * FROM (SELECT * FROM X UNION SELECT * FROM y UNION SELECT * FROM Z) - + Note that many database backends do not allow ORDER BY to be rendered on a query called within UNION, EXCEPT, etc. To disable all ORDER BY clauses including those configured on mappers, issue ``query.order_by(None)`` - the resulting :class:`.Query` object will not render ORDER BY within its SELECT statement. - + """ @@ -1612,7 +1612,7 @@ class Query(object): def distinct(self, *criterion): """Apply a ``DISTINCT`` to the query and return the newly resulting ``Query``. - + :param \*expr: optional column expressions. When present, the Postgresql dialect will render a ``DISTINCT ON (<expressions>>)`` construct. @@ -2035,73 +2035,42 @@ class Query(object): def count(self): """Return a count of rows this Query would return. + + This generates the SQL for this Query as follows:: + + SELECT count(1) AS count_1 FROM ( + SELECT <rest of query follows...> + ) AS anon_1 - For simple entity queries, count() issues - a SELECT COUNT, and will specifically count the primary - key column of the first entity only. If the query uses - LIMIT, OFFSET, or DISTINCT, count() will wrap the statement - generated by this Query in a subquery, from which a SELECT COUNT - is issued, so that the contract of "how many rows - would be returned?" is honored. - - For queries that request specific columns or expressions, - count() again makes no assumptions about those expressions - and will wrap everything in a subquery. Therefore, - ``Query.count()`` is usually not what you want in this case. - To count specific columns, often in conjunction with - GROUP BY, use ``func.count()`` as an individual column expression - instead of ``Query.count()``. See the ORM tutorial - for an example. - + Note the above scheme is newly refined in 0.7 + (as of 0.7b3). + + For fine grained control over specific columns + to count, to skip the usage of a subquery or + otherwise control of the FROM clause, + or to use other aggregate functions, + use :attr:`.func` expressions in conjunction + with :meth:`~.Session.query`, i.e.:: + + from sqlalchemy import func + + # count User records, without + # using a subquery. + session.query(func.count(User.id)) + + # return count of user "id" grouped + # by "name" + session.query(func.count(User.id)).\\ + group_by(User.name) + + from sqlalchemy import distinct + + # count distinct "name" values + session.query(func.count(distinct(User.name))) + """ - should_nest = [self._should_nest_selectable] - def ent_cols(ent): - if isinstance(ent, _MapperEntity): - return ent.mapper.primary_key - else: - should_nest[0] = True - return [ent.column] - - return self._col_aggregate(sql.literal_column('1'), sql.func.count, - nested_cols=chain(*[ent_cols(ent) for ent in self._entities]), - should_nest = should_nest[0] - ) - - def _col_aggregate(self, col, func, nested_cols=None, should_nest=False): - context = QueryContext(self) - - for entity in self._entities: - entity.setup_context(self, context) - - if context.from_clause: - from_obj = list(context.from_clause) - else: - from_obj = context.froms - - if self._enable_single_crit: - self._adjust_for_single_inheritance(context) - - whereclause = context.whereclause - - if should_nest: - if not nested_cols: - nested_cols = [col] - else: - nested_cols = list(nested_cols) - s = sql.select(nested_cols, whereclause, - from_obj=from_obj, use_labels=True, - **self._select_args) - s = s.alias() - s = sql.select( - [func(s.corresponding_column(col) or col)]).select_from(s) - else: - s = sql.select([func(col)], whereclause, from_obj=from_obj, - **self._select_args) - - if self._autoflush and not self._populate_existing: - self.session._autoflush() - return self.session.scalar(s, params=self._params, - mapper=self._mapper_zero()) + col = sql.func.count(sql.literal_column('1')) + return self.from_self(col).scalar() def delete(self, synchronize_session='evaluate'): """Perform a bulk delete query. |
