SQLAlchemy Select 和 Query API 之间的区别

问题描述 投票:0回答:3

不确定以前是否有人问过这个问题,但在 SQLAlchemy docs 中,他们谈论引入

select()
作为 ORM 的新
2.0 style
的一部分。 之前(
1.x style
),使用
query()
方法来获取数据。 这两者有什么区别?

例如,要查询

Users
表中包含电子邮件和姓名的用户,我们可以在查询 API 中执行以下操作:

session.query(Users).filter_by(name='name', email='[email protected]').first()

在 Select API 中,同样会导致更多代码:

from sqlalchemy import select

query = select(Users).filter_by(name='name', email='[email protected]')
user = session.execute(query).fetchone()

与其他相比,使用其中一种是否有任何显着优势,例如性能提升? 2.0 API 仍在积极开发中,但似乎他们的文档更倾向于选择 API,而不是“传统”查询 API。这只是试图弥合 ORM 和核心功能之间的差距吗?

python sql sqlalchemy orm
3个回答
15
投票

最大的区别在于

select
语句的构造方式。新方法创建一个
select
对象
,它更加动态,因为它可以从其他 select 语句构造,无需显式子查询定义:

# select from a subqeuery styled query
q = select(Users).filter_by(name='name', email='[email protected]')
q = select(Users.name, Users.email).select_from(q)

结果是根据最新的可选 API,更加“原生 SQL”的查询构造。可以在各种功能的语句中定义和传递查询,例如 where 子句、having、select_from、intersect、union 等。

性能方面,可能在 python 运行时(编译查询)方面有一些轻微的好处,但与网络延迟 + 数据库工作相比可以忽略不计。

💡顺便说一句,好问题!我的回答是根据我使用 select API 的经验得出的。我很好奇听听其他人怎么说。


5
投票

从 1.4 开始,SQLAlchemy 内部已经通过 select() API 实现了 query(),所以在性能方面应该差别很小。

在 1.4 版本中,所有 Core 和 ORM SELECT 语句均从 直接选择对象;当使用 Query 对象时,at 语句 调用时,它将其状态复制到 Select,然后调用 内部使用 2.0 风格执行。

https://docs.sqlalchemy.org/en/14/changelog/migration_14.html#change-5159

从历史上看,query() 和 select() 之间的区别是 query() 用于 ORM,而 select() 用于 Core。 2.0 版本消除了 ORM 和 Core 之间的许多差异,并使它们的使用更加统一。比较 select() 和 query() 已经没有意义了。

虽然有一些向后兼容性,并且您不必立即采用 2.0 风格,但我认为在 1.4 和 2.0 中开始采用它是明智的。我这样做已经有一段时间了,发现它很容易习惯,而且与 1.x 风格相比很快就更直观了。但我现在只使用 SQLAlchemy 大约一年,并且拥有多年的原生 SQL 经验。


0
投票

这个问题的“愚蠢”答案是

  • 较新的
    select().where()
    方法很难使用
  • 旧的
    query().filter_by()
    方法很容易使用

让我通过一个例子来解释一下:

'''
    This demonstrates the "old" way of querying
'''

#!/usr/bin/env python3

from sqlalchemy import Engine
from sqlalchemy import create_engine
from sqlalchemy import select

from sqlalchemy.orm import Session
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column


class Base(DeclarativeBase):
    pass


class SimpleTable(Base):
    __tablename__ = 'simple_table'

    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    number: Mapped[int]


def main():

    url = 'postgresql://postgres:password@ipaddress/postgres'
    engine = create_engine(url)

    create_tables(engine)

    with Session(engine) as session:

        simple_table_rows = session \
            .query(SimpleTable) \
            .filter_by(name='name1') \
            .all()

        for simple_table_row in simple_table_rows:
            print(f'name: {simple_table_row.name}, number: {simple_table_row.number}')


if __name__ == '__main__':
    main()

将此与新方法进行对比,新方法中我们需要额外的

simple_table_row = simple_table_row[0]
语句来将仅包含单个元素的元组转换为我们实际想要的类型。 (这是
SimpleTableRow
。)

'''
    This demonstrates the "new" way of querying
'''

    with Session(engine) as session:

        simple_table_rows = session.execute(
            select(SimpleTable)
            .where(SimpleTable.name=='name1')
        ).all()

        for simple_table_row in simple_table_rows:

            simple_table_row = simple_table_row[0]

            print(f'name: {simple_table_row.name}, number: {simple_table_row.number}')

我们可以检查返回的“thing”确实是一个元组。

        for simple_table_row in simple_table_rows:

            print(type(simple_table_row))
            print(f'')

            for d in dir(simple_table_row):
                if not d.startswith('__'):
                    print(d)

打印此输出:

<class 'sqlalchemy.engine.row.Row'>

_abc_impl
_asdict
_data
_fields
_filter_on_values
_get_by_key_impl_mapping
_is_protocol
_key_to_index
_mapping
_op
_parent
_special_name_accessor
_t
_to_tuple_instance
_tuple
_values_impl
count
index
t
tuple

我并不是 100% 明白为什么需要额外的“获取元组的 0 索引元素”。

我猜测新的

select()
语法允许对数据库进行更复杂的查询,例如通过一次选择多个对象,也许作为连接或其他内容的一部分。

然而,对于大多数简单的用例来说,它是不必要的,只是因为必须记住拨打

row = row[0]
而带来额外的小不便。

我建议您在大多数情况下使用“旧”样式。

SQL Alchemy 的一个主要问题是它作为一个库有多复杂。

编写代码时通常有不止一种方法来完成某些任务。然而,通常只有一种“明显”的好方法。

使用 SQL ALchemy,有多种不同的方法可以做完全相同的事情,并且文档并没有真正解释哪种方法是最明智的方法。

它告诉我们有一种“旧”方法和一种“新”方法,还有一种“较旧的旧方法”,仅适用于版本 1.4 等...

文档很长,但我不确定它的长篇大论是否能帮助我们理解如何执行常见的操作,这应该是相对简单的。

© www.soinside.com 2019 - 2024. All rights reserved.