【问题标题】:How to count rows with SELECT COUNT(*) with SQLAlchemy?如何使用 SQLAlchemy 使用 SELECT COUNT(*) 计算行数?
【发布时间】:2012-10-08 03:07:09
【问题描述】:

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

session.query(table).count()

然后它会生成类似的东西:

SELECT count(*) AS count_1 FROM
    (SELECT table.col1 as col1, table.col2 as col2, ... from table)

在带有 InnoDB 的 MySQL 中速度明显较慢。我正在寻找一种不需要表具有已知主键的解决方案,正如Get the number of rows in table using SQLAlchemy 中所建议的那样。

【问题讨论】:

标签: python sql sqlalchemy


【解决方案1】:

以下是查找任何查询计数的方法。

aliased_query = alias(query)
db.session.query(func.count('*')).select_from(aliased_query).scalar()

如果您想探索更多选项或阅读详细信息,这里是link to the reference document

【讨论】:

  • 这仍然将别名查询包装到子选择中。没有名称的别名生成只是一个匿名子选择。您的答案生成的 SQL 与提问者想要避免的 SQL 完全相同
【解决方案2】:

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

SELECT count(*) AS count_1
FROM "table"

从 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)

ORM 层的使用

你只需继承 Query(你可能无论如何都有)并提供一个专门的 count() 方法,就像这个。

from sqlalchemy.sql.expression 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()

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

使用此方法,您可以在任何 ORM 查询上使用 count(*),这将遵循已指定的所有 filterjoin 条件。

【讨论】:

  • 非常感谢!似乎有点令人费解,但它确实有效:) 在接受之前,我会等一下,看看是否有人提出了更简单的语法。
  • 糟糕,对不起,我错了,它实际上不起作用。我的意思是,它按照你说的做,但它仍然没有产生SELECT(*)(在我的实际用例中,它的速度大约是你建议的两倍)。
  • 我修改了我的例子。如果确实需要“select count(*)”,可以通过“text”表达式实现。
  • 使用 sqlalchemy.sql.func.count() 代替 sqlalchemy.sql.text('count(*)')
  • 对于简单的select ... from table 查询,这可能会从查询中删除表。
【解决方案3】:

除了接受答案中的ORM 层的使用:count(*) 可以使用query.with_entities(func.count()) 为ORM 完成,如下所示:

session.query(MyModel).with_entities(func.count()).scalar()

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

例如:

  • 我们有User模型(idname)和Song模型(idtitlegenre
  • 我们有用户歌曲数据 - UserSong 模型(user_idsong_idis_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'

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

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()

完整的例子是here

【讨论】:

  • 这只能用于使用 .join() 的查询。否则,查询将被“优化”并且 WHERE 子句被简单地删除。例如session.query(Model).with_entities(func.count()).scalar() 生成SELECT count(*) AS count_1 我怀疑有人在搜索^^的结果。
  • @RomainVincent 对我来说,session.query(MyModel).filter(MyModel.rank < 100).with_entities(func.count()) 生成 SELECT count(*) AS count_1 FROM my_table WHERE my_table.rank < %(rank_1)s,所以你可能会遇到一些极端情况或错误。
  • 是的,我过度概括了我的回复。所以一个更好的说法是“如果你没有明确使用 query() 参数中的列,这将不起作用。我必须承认我不知道 SQLA 会做什么,但是在我看来,这种方法使用起来似乎很危险,因为它会导致意想不到的结果。
  • Query.with_entities() 基本上与Query.statement.with_only_columns() 做同样的事情(如在接受的答案中),并且对于普通的session.query(Model) 有同样的问题,这会产生SELECT COUNT(*)(没有@987654350 @)。在gerrit.sqlalchemy.org/c/sqlalchemy/sqlalchemy/+/2973 登陆之前,在 ORM 级别没有很好的方法来做到这一点。
【解决方案4】:

如果您使用 SQL 表达式样式方法,如果您已经拥有表对象,则还有另一种方法来构造计数语句。

准备获取表格对象。也有不同的方法。

import sqlalchemy

database_engine = sqlalchemy.create_engine("connection string")

# Populate existing database via reflection into sqlalchemy objects
database_metadata = sqlalchemy.MetaData()
database_metadata.reflect(bind=database_engine)

table_object = database_metadata.tables.get("table_name") # This is just for illustration how to get the table_object                    

table_object上发出计数查询

query = table_object.count()
# This will produce something like, where id is a primary key column in "table_name" automatically selected by sqlalchemy
# 'SELECT count(table_name.id) AS tbl_row_count FROM table_name'

count_result = database_engine.scalar(query)

【讨论】:

    【解决方案5】:

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

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

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

    from sqlalchemy import func
    
    session.query(func.count(User.id)).scalar() 
    

    【讨论】:

    【解决方案6】:

    只查询一个已知列:

    session.query(MyTable.col1).count()
    

    【讨论】:

    • 这会将计数构造为一个包装的选择。 MySQL 的查询优化器应该会处理它。
    • 谢谢:虽然它不能完全回答我的问题,但确实更快。我担心它会忽略NULL 条目,但事实并非如此。但是,它仍然比显式的 SELECT(*) 慢。
    • @Nathan Villaescusa 您能否提供多列的示例?例如:session.query(MyTable.col1, MyTable.col2).count()。 sql:SELECT col1, COUNT(col1) FROM "table"
    猜你喜欢
    • 1970-01-01
    • 2015-04-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-25
    • 2018-05-21
    相关资源
    最近更新 更多