如何使用SQLAlchemy使用SELECT COUNT(*)计算行数?

tih*_*iho 44 python sql sqlalchemy

我想知道是否可以SELECT COUNT(*) FROM TABLE在SQLAlchemy中生成一个语句,而无需明确要求它execute().如果我使用:

session.query(table).count()

然后它产生类似的东西:

SELECT count(*) AS count_1 FROM
    (SELECT table.col1 as col1, table.col2 as col2, ... from table)
Run Code Online (Sandbox Code Playgroud)

使用InnoDB在MySQL中显着变慢.我正在寻找一个不需要表具有已知主键的解决方案,如使用SQLAlchemy获取表中的行数所示.

pi.*_*pi. 58

我设法在两个层上使用SQLAlchemy呈现以下SELECT.

SELECT count(*) AS count_1
FROM "table"
Run Code Online (Sandbox Code Playgroud)

SQL表达式层的用法

from sqlalchemy import select, func, Integer, Table, Column, MetaData

metadata = MetaData()

table = Table("table", metadata,
              Column('primary_key', Integer),
              Column('other_column', Integer)  # just to illustrate
             )   

print select([func.count()]).select_from(table)
Run Code Online (Sandbox Code Playgroud)

ORM层的用法

你只是子类Query(你可能还有)并提供一个专门的count()方法,就像这个.

from sqlalchemy import func

class BaseQuery(Query):
    def count_star(self):
        count_query = (self.statement.with_only_columns([func.count()])
                       .order_by(None))
        return self.session.execute(count_query).scalar()
Run Code Online (Sandbox Code Playgroud)

请注意,order_by(None)重置查询的顺序,这与计数无关.

使用这种方法,你可以有count(*)任何的ORM查询,将兑现所有的filterjoin已经规定的条件.

  • 对于简单的“select ... from table”查询,这可能会从查询中“删除”该表。 (2认同)

Nat*_*usa 41

仅查询单个已知列:

session.query(MyTable.col1).count()
Run Code Online (Sandbox Code Playgroud)

  • 这将把计数构造为包装选择.MySQL的查询优化器应该照顾它. (4认同)
  • 谢谢:虽然它没有完全回答我的问题,但确实更快.我担心它会忽略`NULL`条目,但事实并非如此.但是,它仍然比显式的`SELECT(*)`慢. (2认同)

mel*_*r55 21

我需要用很多连接来计算一个非常复杂的查询.我使用连接作为过滤器,所以我只想知道实际对象的数量.count()不足,但我在这里的文档中找到了答案:

http://docs.sqlalchemy.org/en/latest/orm/tutorial.html

代码看起来像这样(计算用户对象):

from sqlalchemy import func

session.query(func.count(User.id)).scalar() 
Run Code Online (Sandbox Code Playgroud)

  • 您的示例中没有任何地方引用了任何查询...... (3认同)

Bor*_*rov 11

除了接受的答案中ORM 层Usage 之外:count(*) can be done for ORM using the query.with_entities(func.count()),如下所示:

session.query(MyModel).with_entities(func.count()).scalar()
Run Code Online (Sandbox Code Playgroud)

它也可以用于更复杂的情况,当我们有连接和过滤器时 - 这里重要的是放在with_entities连接之后,否则 SQLAlchemy 可能会引发Don't know how to join错误。

例如:

  • 我们有User模型 ( id, name) 和Song模型 ( id, title, genre)
  • 我们有用户歌曲数据 -UserSong模型 ( user_id, song_id, is_liked) 其中user_id+song_id是主键)

我们想要获得一些用户喜欢的摇滚歌曲:

SELECT count(*) 
  FROM user_song
  JOIN song ON user_song.song_id = song.id 
 WHERE user_song.user_id = %(user_id)
   AND user_song.is_liked IS 1
   AND song.genre = 'rock'
Run Code Online (Sandbox Code Playgroud)

此查询可以通过以下方式生成:

user_id = 1

query = session.query(UserSong)
query = query.join(Song, Song.id == UserSong.song_id)
query = query.filter(
    and_(
        UserSong.user_id == user_id, 
        UserSong.is_liked.is_(True),
        Song.genre == 'rock'
    )
)
# Note: important to place `with_entities` after the join
query = query.with_entities(func.count())
liked_count = query.scalar()
Run Code Online (Sandbox Code Playgroud)

完整的例子在这里

  • 这仅适用于使用 .join() 的查询。否则,查询将被“优化”并且 WHERE 子句将被简单地删除。例如`session.query(Model).with_entities(func.count()).scalar()`生成`SELECT count(*) AS count_1`,我怀疑有人在搜索^^的结果。 (3认同)
  • `Query.with_entities()` 本质上与 `Query.statement.with_only_columns()` 做同样的事情(如接受的答案中所示),并且对于普通的 `session.query(Model)` 产生同样的问题“SELECT COUNT(*)”(无“FROM”)。在 https://gerrit.sqlalchemy.org/c/sqlalchemy/sqlalchemy/+/2973 落地之前,在 ORM 级别上没有好的方法来做到这一点。 (2认同)

Kon*_*nin 11

这不是十多年前发布的原始问题的答案,而是“如何进行SELECT COUNT(*) ”问题的一般答案。另请注意,这里的许多答案都非常过时,仅适用于旧的 SQLAlchemy 版本。

以下是 SQLAlchemy 1.4.x 和 2.0.x 的解决方案:

from sqlalchemy import func, select

class MyModel(Base):
    ...

statement = select(func.count()).select_from(MyModel)
count: int = session.execute(statement).scalar()
Run Code Online (Sandbox Code Playgroud)