What is the equivalent of Query.count in the SQLAlchemy 1.4 ORM?
Question:
Given a SQLAlchemy ORM model like this
class Foo(Base):
__tablename__ = 'foo'
id = sa.Column(sa.Integer, primary_key=True)
name = sa.Column(sa.String())
In SQLAlchemy 1.4 / 2.0, the ORM’s session.query
idiom is being unified with the SQLAlchemy core select
function*, so to fetch all Foo
s we would do
foos = session.execute(Foo).scalars().all()
instead of
foos = session.query(Foo).all()
In the current (sqlalchemy<=1.3) ORM we can obtain the number of Foo
s in the database with this query:
nfoos = session.query(Foo).count()
But how can we get the count in SQLALchemy 1.4?
session.execute(sa.select(Foo).count())
raises
AttributeError: ‘Select’ object has no attribute ‘count’
session.execute(sa.select(Foo)).count()
raises
AttributeError: ‘ChunkedIteratorResult’ object has no attribute ‘count’
session.execute(sa.select(sa.func.count(Foo)))
raises
sqlalchemy.exc.ArgumentError: SQL expression element expected, got <class '__main__.Foo'>
.
This works,
session.execute(sa.select(sa.func.count(Foo.id))).scalars()
but specifying an attribute seems less OO / elegant than the Query.count
version. Moreover, it precludes building a query but deferring the decision of whether to retrieve the count or the model instances.
What is the idiomatic way to get the count()
of an ORM query in the new SQLAlchemy 1.4 world?
* the session.query()
API will still work in 1.4 and later
Answers:
Based on Ilja Everilä‘s comments, it seems that there is no direct equivalent to Query.count in the new ORM-querying API released in SQLAlchemy 1.4 (in beta at the time of writing).
The functional equivalent is to call count()
, selecting from a subquery*
from sqlalchemy import func, select
count = (
session.execute(select(func.count()).select_from(select(Foo).subquery()))
.scalar_one()
)
Generating this SQL
SELECT count(*) AS count_1
FROM (SELECT foo.id AS id, foo.name AS name
FROM foo)
AS anon_1
Counting an attribute, such as the primary key column, generates a simple SELECT COUNT
count = session.execute(select(func.count(Foo.id))).scalar_one()
SELECT count(foo.id) AS count_1
FROM foo
Query.count
is still supported in 1.4, so it can still be used, as can all the ORM features available in 1.3.
* Query.count
also selects from a subquery rather than executing SELECT COUNT
directly.
If you have constructed a complex query
with joins and several conditions, you can count the query results like this:
count_query = query.with_only_columns(func.count(Foo.id))
count = session.execute(count_query).scalar_one()
The idea is to use a copy of the query and replace the selected columns with a single count column.
You can use rowcount
# using sqlalchemy
from sqlalchemy import create_engine
def connect_to_db():
"""Create database connection."""
url = f"mysql+pymysql://{user}:{db_pwd}@{host}:{port}/{db_name}"
try:
engine = create_engine(url)
return engine.connect()
except Exception as e:
raise e
def execute_query(query):
connection = connect_to_db()
with connection as con:
result = con.execute(query)
return result
results = execute_query("show tables;")
print("count = ",results.rowcount)
output: count = 1298
For me – using Flask-Sqlalchemy, with the SQLAlchemy Version 2.X – this worked:
subselect = db.select(Tablename).filter_by(user_id=current_user.id, ...)
select = db.select(db.func.count()).select_from(subselect)
number_of_rows = db.session.execute(query).scalar_one()
Shoutout to @snakecharmerb – on strike
for giving the answer to his/her own question and really helping me out there!
Given a SQLAlchemy ORM model like this
class Foo(Base):
__tablename__ = 'foo'
id = sa.Column(sa.Integer, primary_key=True)
name = sa.Column(sa.String())
In SQLAlchemy 1.4 / 2.0, the ORM’s session.query
idiom is being unified with the SQLAlchemy core select
function*, so to fetch all Foo
s we would do
foos = session.execute(Foo).scalars().all()
instead of
foos = session.query(Foo).all()
In the current (sqlalchemy<=1.3) ORM we can obtain the number of Foo
s in the database with this query:
nfoos = session.query(Foo).count()
But how can we get the count in SQLALchemy 1.4?
session.execute(sa.select(Foo).count())
raises
AttributeError: ‘Select’ object has no attribute ‘count’
session.execute(sa.select(Foo)).count()
raises
AttributeError: ‘ChunkedIteratorResult’ object has no attribute ‘count’
session.execute(sa.select(sa.func.count(Foo)))
raises
sqlalchemy.exc.ArgumentError: SQL expression element expected, got
<class '__main__.Foo'>
.
This works,
session.execute(sa.select(sa.func.count(Foo.id))).scalars()
but specifying an attribute seems less OO / elegant than the Query.count
version. Moreover, it precludes building a query but deferring the decision of whether to retrieve the count or the model instances.
What is the idiomatic way to get the count()
of an ORM query in the new SQLAlchemy 1.4 world?
* the session.query()
API will still work in 1.4 and later
Based on Ilja Everilä‘s comments, it seems that there is no direct equivalent to Query.count in the new ORM-querying API released in SQLAlchemy 1.4 (in beta at the time of writing).
The functional equivalent is to call count()
, selecting from a subquery*
from sqlalchemy import func, select
count = (
session.execute(select(func.count()).select_from(select(Foo).subquery()))
.scalar_one()
)
Generating this SQL
SELECT count(*) AS count_1
FROM (SELECT foo.id AS id, foo.name AS name
FROM foo)
AS anon_1
Counting an attribute, such as the primary key column, generates a simple SELECT COUNT
count = session.execute(select(func.count(Foo.id))).scalar_one()
SELECT count(foo.id) AS count_1
FROM foo
Query.count
is still supported in 1.4, so it can still be used, as can all the ORM features available in 1.3.
* Query.count
also selects from a subquery rather than executing SELECT COUNT
directly.
If you have constructed a complex query
with joins and several conditions, you can count the query results like this:
count_query = query.with_only_columns(func.count(Foo.id))
count = session.execute(count_query).scalar_one()
The idea is to use a copy of the query and replace the selected columns with a single count column.
You can use rowcount
# using sqlalchemy
from sqlalchemy import create_engine
def connect_to_db():
"""Create database connection."""
url = f"mysql+pymysql://{user}:{db_pwd}@{host}:{port}/{db_name}"
try:
engine = create_engine(url)
return engine.connect()
except Exception as e:
raise e
def execute_query(query):
connection = connect_to_db()
with connection as con:
result = con.execute(query)
return result
results = execute_query("show tables;")
print("count = ",results.rowcount)
output: count = 1298
For me – using Flask-Sqlalchemy, with the SQLAlchemy Version 2.X – this worked:
subselect = db.select(Tablename).filter_by(user_id=current_user.id, ...)
select = db.select(db.func.count()).select_from(subselect)
number_of_rows = db.session.execute(query).scalar_one()
Shoutout to @snakecharmerb – on strike
for giving the answer to his/her own question and really helping me out there!