使用引擎和接头

优质
小牛编辑
119浏览
2023-12-01

本节详细介绍了 EngineConnection 以及相关对象。重要的是要注意,当使用sqlAlchemy ORM时,通常不会访问这些对象;相反,这些对象 Session 对象用作数据库的接口。但是,对于围绕直接使用文本SQL语句和/或SQL表达式构造而构建的应用程序,而不需要ORM的更高级别管理服务的参与,则 EngineConnection 是国王(还是王后?)-继续阅读。

基本用法

回忆起 引擎配置 那是 Engine 通过创建 create_engine() 呼叫:

engine = create_engine('mysql://scott:tiger@localhost/test')

的典型用法 create_engine() 是每个特定数据库URL一次,在单个应用程序进程的生命周期内全局保存。单一的 Engine 管理许多个人 DBAPI 代表进程的连接,并打算以并发方式调用。这个 Enginenot 与DBAPI同义 connect 函数,它只表示一个连接资源 Engine 仅在应用程序的模块级创建一次,而不是每个对象或每个函数调用时,效率最高。

最基本的功能 Engine 提供访问 Connection ,然后可以调用SQL语句。向数据库发出文本语句如下所示:

from sqlalchemy import text

with engine.connect() as connection:
    result = connection.execute(text("select username from users"))
    for row in result:
        print("username:", row['username'])

上面, Engine.connect() 方法返回 Connection 对象,并在Python上下文管理器中使用它(例如 with: 声明)the Connection.close() 方法在块末尾自动调用。这个 Connection 是一个 代理 对象获取实际的DBAPI连接。DBAPI连接是在 Connection 创建。

返回的对象称为 CursorResult ,它引用DBAPI游标,并提供用于获取与DBAPI游标类似的行的方法。DBAPI光标将由 CursorResult 当它的所有结果行(如果有)用完时。一 CursorResult 它不返回任何行,例如UPDATE语句的行(不返回任何行),在构造后立即释放光标资源。

Connection 在结束时关闭 with: 块,引用的DBAPI连接是 released 到连接池。从数据库本身的角度来看,假设连接池有空间存储此连接以备下次使用,那么连接池实际上不会“关闭”连接。当连接返回池以供重新使用时,池机制将发出一个 rollback() 调用DBAPI连接,以便删除所有事务状态或锁,并且该连接已准备好供下次使用。

2.0 版后已移除: 这个 CursorResult 对象在SQLAlchemy 2.0中被替换为 Result .

上面的示例演示了文本SQL字符串的执行,应该使用 text() 构造来指示我们希望使用文本SQL。这个 Connection.execute() 方法当然可以容纳更多的内容,包括中描述的各种SQL表达式构造。 SQL表达式语言教程(1.x API) .

使用事务

注解

本节介绍如何在直接处理事务时使用事务 EngineConnection 物体。使用SQLAlchemy ORM时,用于事务控制的公共API通过 Session 对象,它使用 Transaction 对象内部。见 管理交易 更多信息。

这个 Connection 对象提供 Connection.begin() 返回 Transaction 对象。就像 Connection 它本身通常在Python中使用 with: 块以便管理其作用域:

with engine.connect() as connection:
    with connection.begin():
        r1 = connection.execute(table1.select())
        connection.execute(table1.insert(), {"col1": 7, "col2": "this is some data"})

上面的块可以通过使用 Engine.begin() 方法 Engine ::

# runs a transaction
with engine.begin() as connection:
    r1 = connection.execute(table1.select())
    connection.execute(table1.insert(), {"col1": 7, "col2": "this is some data"})

各自管理的区块 .begin() 方法的行为使得在块完成时提交事务。如果引发异常,则会回滚事务,并向外传播异常。

用于表示事务的基础对象是 Transaction 对象。此对象由 Connection.begin() 方法,包括方法 Transaction.commit()Transaction.rollback() . 推荐使用上下文管理器调用表单(它自动调用这些方法)作为最佳实践。

事务块的嵌套

1.4 版后已移除: SQLAlchemy的“事务嵌套”特性是一个在1.4版本中不推荐使用的遗留特性,它将在SQLAlchemy 2.0中删除。这个模式已经被证明有点太笨拙和复杂了,除非应用程序围绕行为创建了更多的一流框架。见以下小节 作为反模式的任意事务嵌套 .

这个 Transaction 对象还通过跟踪最外层的begin/commit对来处理“嵌套”行为。在本例中,两个函数都在 Connection 但只有最外面的 Transaction 对象在提交时实际生效。

# method_a starts a transaction and calls method_b
def method_a(connection):
    with connection.begin():  # open a transaction
        method_b(connection)

# method_b also starts a transaction
def method_b(connection):
    with connection.begin(): # open a transaction - this runs in the
                             # context of method_a's transaction
        connection.execute(text("insert into mytable values ('bat', 'lala')"))
        connection.execute(mytable.insert(), {"col1": "bat", "col2": "lala"})

# open a Connection and call method_a
with engine.connect() as conn:
    method_a(conn)

上面, method_a 先被调用,它调用 connection.begin() . 然后它呼叫 method_b . 什么时候? method_b 电话 connection.begin() ,它只是递增一个计数器,当它调用 commit() . 如果任一 method_amethod_b 电话 rollback() ,将回滚整个事务。在以下时间之前未提交事务 method_a 调用 commit() 方法。这种“嵌套”行为允许创建功能,这些功能“保证”如果一个事务不可用,将使用该事务,但如果一个事务存在,将自动参与封闭事务。

作为反模式的任意事务嵌套

有了多年的经验,上面的“嵌套”模式并没有被证明是非常流行的,而且在Openstack这样的大型项目中已经观察到,它往往是复杂的。

组织应用程序的最理想方法是只有一个或至少很少的一个点来划分所有数据库事务的“开始”和“提交”。这也是ORM在 我什么时候做一个 Session ,什么时候提交,什么时候关闭? . 要使上一节中的示例适应此实践,如下所示:

# method_a calls method_b
def method_a(connection):
    method_b(connection)

# method_b uses the connection and assumes the transaction
# is external
def method_b(connection):
    connection.execute(text("insert into mytable values ('bat', 'lala')"))
    connection.execute(mytable.insert(), {"col1": "bat", "col2": "lala"})

# open a Connection inside of a transaction and call method_a
with engine.begin() as conn:
    method_a(conn)

也就是说, method_a()method_b() 根本不要处理事务的细节;连接的事务作用域是定义的 外部的 与连接具有SQL对话的函数。

可以看出,上面的代码行数较少,缩进较少,这往往与较低的代码相关 cyclomatic complexity . 上述代码的组织方式如下: method_a()method_b() 总是从事务开始的点调用。该示例的前一个版本具有一个 method_a() 和A method_b() 他们试图对这一事实保持不可知论,这表明他们已经准备好应对至少两倍于他们的潜在代码路径。

从“嵌套”模式迁移

由于SQLAlchemy的内在嵌套模式被认为是遗留的,一个由于遗留或新颖的原因仍然寻求拥有一个能够自动构建事务的上下文的应用程序,应该通过使用自定义Python上下文管理器来维护该功能。下面的“seealso”部分也提供了一个类似的ORM示例。

为了为使用此模式的应用程序提供向后兼容性,可以使用以下上下文管理器或基于装饰器的类似实现::

import contextlib

@contextlib.contextmanager
def transaction(connection):
    if not connection.in_transaction():
        with connection.begin():
            yield connection
    else:
        yield connection

上面的contextmanager将用作:

# method_a starts a transaction and calls method_b
def method_a(connection):
    with transaction(connection):  # open a transaction
        method_b(connection)

# method_b either starts a transaction, or uses the one already
# present
def method_b(connection):
    with transaction(connection):  # open a transaction
        connection.execute(text("insert into mytable values ('bat', 'lala')"))
        connection.execute(mytable.insert(), {"col1": "bat", "col2": "lala"})

# open a Connection and call method_a
with engine.connect() as conn:
    method_a(conn)

也可以采取类似的方法,以便根据需要建立连接;下面的方法具有一个一次性上下文管理器,它访问封闭状态,以测试连接是否已经存在:

import contextlib

def connectivity(engine):
    connection = None

    @contextlib.contextmanager
    def connect():
        nonlocal connection

        if connection is None:
            connection = engine.connect()
            with connection:
                with connection.begin():
                    yield connection
        else:
            yield connection

    return connect

使用上面的方法看起来像:

# method_a passes along connectivity context, at the same time
# it chooses to establish a connection by calling "with"
def method_a(connectivity):
    with connectivity():
        method_b(connectivity)

# method_b also wants to use a connection from the context, so it
# also calls "with:", but also it actually uses the connection.
def method_b(connectivity):
    with connectivity() as connection:
        connection.execute(text("insert into mytable values ('bat', 'lala')"))
        connection.execute(mytable.insert(), {"col1": "bat", "col2": "lala"})

# create a new connection/transaction context object and call
# method_a
method_a(connectivity(engine))

上面的上下文管理器不仅充当一个“事务”上下文,而且还作为一个上下文来管理与特定的 Engine . 使用ORM时 Session ,此连接管理由 Session 本身。ORM连接模式概述见 管理交易 .

参见

从“子事务”模式迁移 -ORM版本

库级(例如仿真)自动提交

1.4 版后已移除: SQLAlchemy Core的“autocommit”功能已被弃用,并且不会出现在SQLAlchemy的2.0版本中。DBAPI级别的AUTOCOMMIT现在已经广泛使用,它提供了优异的性能,并且透明地进行。看到了吗 库级(但不是驱动程序级)“自动提交”从核心和ORM中删除 背景。

注解

本节讨论SQLAlchemy中自动调用 .commit() 方法,但这是针对 本身就是事务性的 . 有关真正的自动提交,请参阅下一节 设置事务隔离级别,包括DBAPI Autocommit .

上一个事务示例说明了如何使用 Transaction 以便多个执行可以参与同一事务。如果不使用 Transaction ?虽然一些DBAPI实现提供了各种特殊的“非事务”模式,但根据PEP-0249,DBAPI的核心行为是 事务始终在进行中 ,仅提供 rollback()commit() 方法但不 begin() . SQLAlchemy假定任何给定DBAPI都是这样。

考虑到这一需求,SQLAlchemy实现了自己的“自动提交”功能,该功能可以在所有后端完全一致地工作。这是通过检测表示数据更改操作的语句(即插入、更新、删除)以及数据定义语言(DDL)语句(如创建表、更改表)来实现的,然后在没有事务进行时自动发出提交。检测是基于 autocommit=True 语句的执行选项。如果该语句是纯文本语句且未设置标志,则使用正则表达式检测特定后端的插入、更新、删除以及各种其他命令:

conn = engine.connect()
conn.execute(text("INSERT INTO users VALUES (1, 'john')"))  # autocommits

“自动提交”功能仅在“否”时有效 Transaction 已另行声明。这意味着该特性通常不与ORM一起使用,因为 Session 默认情况下,对象始终保持 Transaction .

使用Generative可以完全控制“自动提交”行为 Connection.execution_options() 方法提供于 ConnectionEngine ,使用“autocommit”标志打开或关闭所选作用域的自动提交。例如,A text() 表示提交的存储过程的构造可能会使用它,以便select语句将发出commit::

with engine.connect().execution_options(autocommit=True) as conn:
    conn.execute(text("SELECT my_mutating_procedure()"))

设置事务隔离级别,包括DBAPI Autocommit

大多数dbapi支持可配置事务的概念 isolation 水平。这通常是四个级别“未提交读”、“已提交读”、“可重复读”和“可序列化”。它们通常在DBAPI连接开始一个新事务之前应用于它,注意大多数DBAPI将在SQL语句首次发出时隐式地开始该事务。

支持隔离级别的DBAPI通常还支持真正的“自动提交”概念,这意味着DBAPI连接本身将被置于非事务性自动提交模式。这通常意味着自动向数据库发出“BEGIN”的典型DBAPI行为不再发生,但也可能包括其他指令。SQLAlChemy对待“自动提交”的概念与对待任何其他隔离级别一样;因为它是一个不仅失去“读提交”而且失去原子性的隔离级别。

小技巧

值得注意的是,我们将在下面的部分中进一步讨论这一点,地址为 了解DBAPI级别的自动提交隔离级别 与任何其他隔离级别一样,该“自动提交”隔离级别 not 影响的“事务性”行为。 Connection 对象,该对象继续调用DBAPI .commit().rollback() 方法(它们在自动提交下不起作用),对于这些方法, .begin() 方法假定DBAPI将隐式启动事务(这意味着SQLAlChemy的“BEGIN” 不更改自动提交模式 )。

SQLAlchemy方言应该支持这些隔离级别,并尽可能地自动提交。这些级别是通过一系列贯穿核心的“执行选项”参数和方法来设置的,例如 Connection.execution_options() 方法。该参数称为 Connection.execution_options.isolation_level 这些值是字符串,通常是以下名称的子集:

# possible values for Connection.execution_options(isolation_level="<value>")

"AUTOCOMMIT"
"READ COMMITTED"
"READ UNCOMMITTED"
"REPEATABLE READ"
"SERIALIZABLE"

并非每个DBAPI都支持每个值;如果某个后端使用了不受支持的值,则会引发错误。

例如,要在特定连接上强制执行可重复读取,请启动事务:

with engine.connect().execution_options(isolation_level="REPEATABLE READ") as connection:
    with connection.begin():
        connection.execute(<statement>)

注解

对象的返回值 Connection.execution_options() 方法是SQLAlChemy 1.x系列下的所谓“分支”连接,当不使用 create_engine.future 模式,它是原始文件的浅层副本 Connection 对象。尽管如此, isolation_level 执行选项适用于原始 Connection 对象和所有“分支”的整体。

在使用时 create_engine.future 模式(即 2.0 style 用法),这些所谓的“分支”连接的概念被移除,并且 Connection.execution_options() 返回 same Connection 对象,而不创建任何副本。

这个 Connection.execution_options.isolation_level 选项也可以设置为整个发动机范围,这通常是可取的。这是通过在 create_engine.execution_options 参数到 create_engine() ::

from sqlalchemy import create_engine

eng = create_engine(
    "postgresql://scott:tiger@localhost/test",
    execution_options={
        "isolation_level": "REPEATABLE READ"
    }
)

使用上述设置,DBAPI连接将被设置为使用 "REPEATABLE READ" 为开始的每个新事务设置隔离级别。

经常选择在不同隔离级别中运行操作的应用程序可能希望创建一个潜在客户的多个“子引擎” Engine ,每个都将配置为不同的隔离级别。一个这样的用例是一个应用程序,它的操作分为“事务性”和“只读”操作,一个单独的 Engine 它利用了 "AUTOCOMMIT" 可与主机分离:

from sqlalchemy import create_engine

eng = create_engine("postgresql://scott:tiger@localhost/test")

autocommit_engine = eng.execution_options(isolation_level="AUTOCOMMIT")

上面, Engine.execution_options() 方法创建原始的浅拷贝 Engine . 两个 engautocommit_engine 共享相同的方言和连接池。但是,当从获取连接时,“自动提交”模式将在连接上设置 autocommit_engine .

当连接返回到连接池时,将无条件地恢复隔离级别设置(无论是哪一个)。

注解

这个 Connection.execution_options.isolation_level 参数不一定适用于语句级选项,例如 Executable.execution_options() . 这是因为必须在每个事务的基础上在DBAPI连接上设置该选项。

参见

SQLite Transaction Isolation

PostgreSQL Transaction Isolation

MySQL Transaction Isolation

SQL Server Transaction Isolation

设置事务隔离级别/DBAPI AUTOCOMMIT -为ORM

使用DBAPI Autocommit允许透明重新连接的只读版本 -使用DBAPI autocommit透明地重新连接到数据库进行只读操作的配方

了解DBAPI级别的自动提交隔离级别

在父节中,我们介绍了 Connection.execution_options.isolation_level 参数以及如何使用它来设置数据库隔离级别,包括DBAPI级别的“自动提交”,SQLAlChemy将其视为另一个事务隔离级别。在本节中,我们将尝试阐明此方法的含义。

如果我们想要检查一个 Connection 对象,并使用它的“自动提交”模式,我们将按如下方式进行:

with engine.connect() as connection:
    connection.execution_options(isolation_level="AUTOCOMMIT")
    connection.execute(<statement>)
    connection.execute(<statement>)

上面说明了“DBAPI自动提交”模式的正常用法。不需要使用像这样的方法 Connection.begin()Connection.commit() (请注意,后者适用于 2.0 style 用法)。

What's important to note however is that the above autocommit mode is persistent on that particular Connection until we change it directly using isolation_level again. The isolation level is also reset on the DBAPI connection when we release the connection back to the connection pool. However, calling upon Connection.begin() will not change the isolation level, meaning we stay in autocommit. The example below illustrates this:

with engine.connect() as connection:
    connection = connection.execution_options(isolation_level="AUTOCOMMIT")

    # this begin() does nothing, isolation stays at AUTOCOMMIT
    with connection.begin() as trans:
        connection.execute(<statement>)
        connection.execute(<statement>)

当我们在打开日志记录的情况下运行类似上面的挡路时,日志记录将尝试指示虽然dbapi级别 .commit() 由于自动提交模式::

INFO sqlalchemy.engine.Engine BEGIN (implicit)
...
INFO sqlalchemy.engine.Engine COMMIT using DBAPI connection.commit(), DBAPI should ignore due to autocommit mode

同样,在使用 2.0 style create_engine.future 模式,则 Connection 将使用 autobegin 行为,意味着下面的模式将引发错误::

engine = create_engine(..., future=True)

with engine.connect() as connection:
    connection = connection.execution_options(isolation_level="AUTOCOMMIT")

    # "transaction" is autobegin (but has no effect due to autocommit)
    connection.execute(<statement>)

    # this will raise; "transaction" is already begun
    with connection.begin() as trans:
        connection.execute(<statement>)

这一切都是为了演示自动提交隔离级别设置是 completely independent from the begin/commit behavior of the SQLAlchemy Connection object 。“自动提交”模式不会与 Connection.begin() 以任何方式,并且 Connection 在执行与事务相关的自身状态更改时,不会参考此状态(除了在引擎日志记录中建议这些块未实际提交之外)。此设计的基本原理是维护与 Connection 其中,DBAPI自动提交模式可以独立更改,而不会在其他地方指示任何代码更改。

当连接释放回连接池时,隔离级别设置(包括自动提交模式)将自动重置。因此,最好避免尝试在单个 Connection 对象,因为这会导致过多的冗长。

要说明如何在特定模式下使用“自动提交”,请在单个 Connection 结账, Connection.execution_options.isolation_level 参数必须使用以前的隔离级别重新应用。我们可以将上面的挡路“正确”写为(请注意下面的2.0样式用法):

# if we wanted to flip autocommit on and off on a single connection/
# which... we usually don't.

engine = create_engine(..., future=True)

with engine.connect() as connection:

    connection.execution_options(isolation_level="AUTOCOMMIT")

    # run statement(s) in autocommit mode
    connection.execute(<statement>)

    # "commit" the autobegun "transaction" (2.0/future mode only)
    connection.commit()

    # switch to default isolation level
    connection.execution_options(isolation_level=connection.default_isolation_level)

    # use a begin block
    with connection.begin() as trans:
        connection.execute(<statement>)

要手动恢复我们使用的隔离级别,请执行以下操作 Connection.default_isolation_level 恢复默认隔离级别(假设这就是我们这里需要的)。但是,使用的体系结构可能是一个更好的主意 Connection 它已经在签入时自动处理隔离级别的重置。这个 首选 编写上述代码的方法是使用两个块::

engine = create_engine(..., future=True)

# use an autocommit block
with engine.connect().execution_options(isolation_level="AUTOCOMMIT") as connection:

    # run statement in autocommit mode
    connection.execute(<statement>)

# use a regular block
with engine.begin() as connection:
    connection.execute(<statement>)

总结一下:

  1. “DBAPI级别自动提交”隔离级别完全独立于 Connection 对象的“开始”和“提交”概念

  2. 使用个人 Connection 每个隔离级别的签出。避免尝试在单个连接签出时在“自动提交”之间来回更改;让引擎完成恢复默认隔离级别的工作

使用服务器端游标(即流结果)

有限数量的方言明确支持“服务器端游标”与“缓冲游标”的概念。虽然在SQLAlchemy的引擎和方言实现中,服务器端游标意味着各种不同的功能,但它只指特定结果集在从游标中提取之前是否在内存中完全缓冲,方法如下 cursor.fetchall() . SQLAlchemy不直接支持游标行为,如滚动;要将这些特性用于特定的DBAPI,请直接使用游标,如中所述 使用驱动程序SQL和原始DBAPI连接 .

有些DBAPI,如cx峎u Oracle DBAPI,只在内部使用服务器端游标。所有结果集在结果集的整个范围内基本上都是无缓冲区的,只利用固定大小的较小缓冲区,例如一次100行。

对于那些对缓冲或无缓冲结果有条件支持的方言,使用“无缓冲”或服务器端游标模式通常有一些警告。例如,当使用psycopg2方言时,如果服务器端游标与任何类型的DML或DDL语句一起使用,则会引发错误。当使用带有服务器端游标的MySQL驱动程序时,DBAPI连接处于更脆弱的状态,无法从错误条件中正常恢复,也不允许在游标完全关闭之前继续回滚。

因此,SQLAlchemy的方言将始终默认为不易出错的游标版本,这意味着对于PostgreSQL和MySQL方言来说,它默认为一个缓冲的“客户端”游标,在从游标调用任何fetch方法之前,会将完整的结果集拉入内存。这种操作模式适用于 绝大多数 通常情况下,无缓冲游标通常不起作用,除非应用程序以块的形式获取大量行,在这种情况下,可以在提取更多行之前完成对这些行的处理。

要在特定执行中使用服务器端游标,则 Connection.execution_options.stream_results 选项,可以在 Connection 对象、语句对象或下面提到的ORM级别上下文中。

当在语句中使用此选项时,通常应该使用如下方法 Result.partitions() 要一次处理结果集的小部分,同时还要为每次拉取获取足够的行,以便操作高效:

with engine.connect() as conn:
    result = conn.execution_options(stream_results=True).execute(text("select * from table"))

    for partition in result.partitions(100):
        _process_rows(partition)

如果 Result 直接迭代,使用默认的缓冲方案在内部获取行,该方案首先缓冲一小部分行,然后在每次提取时缓冲越来越大的缓冲区,达到预先配置的1000行限制。使用 max_row_buffer 执行选项:

with engine.connect() as conn:
    conn = conn.execution_options(stream_results=True, max_row_buffer=100)
    result = conn.execute(text("select * from table"))

    for row in result:
        _process_row(row)

方法将缓冲区的大小设置为固定大小。 Result.yield_per() 方法。使用多行调用此方法将导致所有结果提取方法都从给定大小的缓冲区中工作,仅在缓冲区为空时才提取新行::

with engine.connect() as conn:
    result = conn.execution_options(stream_results=True).execute(text("select * from table"))

    for row in result.yield_per(100):
        _process_row(row)

这个 stream_results ORM也提供此选项。使用ORM时,可以使用 Result.yield_per()Result.partitions() 方法应用于设置每次产生以下结果时要缓冲的ORM行数:

with orm.Session(engine) as session:
    result = session.execute(
        select(User).order_by(User_id).execution_options(stream_results=True),
    )
    for partition in result.partitions(100):
        _process_rows(partition)

注解

ORM结果集当前必须使用 Result.yield_per()Result.partitions() 以实现流式ORM结果。如果不使用这两种方法中的任何一种来设置在生成之前要提取的行数,则在生成行之前提取整个结果。这在将来的版本中可能会更改,以便由使用的自动缓冲区大小 Connection ORM结果也会发生这种情况。

在使用 1.x style ORM查询,使用 Query ,Year_PER可通过以下方式获得 Query.yield_per() -这还会设置 stream_results 执行选项::

for row in session.query(User).yield_per(100):
    # process row

无连接执行,隐式执行

2.0 版后已移除: SQLAlchemy中的“无连接”和“隐式”执行功能已被弃用,将在2.0版中删除。看到了吗 “隐式”和“无连接”执行,“绑定元数据”已删除 背景。

回想一下我们提到的第一节,执行时使用或不使用 Connection . "“无连接”执行是指 execute() 对象上的方法,该对象不是 Connection . 这是用 Engine.execute() 方法 Engine ::

result = engine.execute(text("select username from users"))
for row in result:
    print("username:", row['username'])

除了“无连接”执行之外,还可以使用 Executable.execute() 任意方法 Executable 构造,它是支持执行的SQL表达式对象的标记。SQL表达式对象本身引用了 EngineConnection 被称为 bind 以提供所谓的“隐式”执行服务。

给出如下表格:

from sqlalchemy import MetaData, Table, Column, Integer

metadata_obj = MetaData()
users_table = Table('users', metadata_obj,
    Column('id', Integer, primary_key=True),
    Column('name', String(50))
)

显式执行将SQL文本或构造的SQL表达式传递到 Connection.execute() 方法 Connection

engine = create_engine('sqlite:///file.db')
with engine.connect() as connection:
    result = connection.execute(users_table.select())
    for row in result:
        # ....

显式无连接执行将表达式传递给 Engine.execute() 方法 Engine

engine = create_engine('sqlite:///file.db')
result = engine.execute(users_table.select())
for row in result:
    # ....
result.close()

隐式执行也是无连接的,并使用 Executable.execute() 方法。此方法是 Executable 类,它引用的SQL语句足以对数据库调用。该方法使用了以下假设: EngineConnection 已经 跳跃 到表达式对象。我们所说的“绑定”是指 MetaData.bind 已用于关联一系列 Table 对象和所有从这些对象派生的具有特定引擎的SQL构造::

engine = create_engine('sqlite:///file.db')
metadata_obj.bind = engine
result = users_table.select().execute()
for row in result:
    # ....
result.close()

上面,我们将 Engine 用一个 MetaData 使用特殊属性的对象 MetaData.bind . 这个 select() 从中生成的构造 Table 对象有一个方法 Executable.execute() ,它将搜索 Engine 这是“绑定”到 Table .

总的来说,“绑定元数据”的使用有三个一般影响:

注解

现代SQLAlchemy没有强调“绑定元数据”和“隐式执行”的概念。虽然它们提供了一些便利,但是它们不再是任何API所需要的,也不再是必需的。

在多个应用程序中 Engine 对象存在,每个对象都与一组特定的表(即 垂直切分 )可以使用“绑定元数据”技术,以便 Table 可以参考适当的 Engine 自动;尤其是通过 Session 对象作为关联方式 Table 对象具有适当的 Engine ,作为使用直接由 Session .

但是,“隐式执行”技术根本不适合与ORM一起使用,因为它绕过了由 Session .

总的来说,在 绝大多数 在案例中,“绑定元数据”和“隐式执行”是 没有用 . 虽然“绑定的元数据”在ORM配置方面的用处微乎其微,但“隐式执行”是一种非常古老的使用模式,在大多数情况下,它比帮助更容易混淆,而且不鼓励使用它。这两种模式似乎都鼓励在应用程序设计中过度使用权宜之计的“捷径”,这将导致后来的问题。

现代的sqlAlchemy用法,尤其是ORM,一直强调在事务的上下文中工作;“隐式执行”概念使得将语句执行与特定事务关联起来的工作更加困难。这个 Executable.execute() 特定SQL语句上的方法通常意味着执行不是任何特定事务的一部分,这通常不是期望的效果。

在两个“无连接”示例中, Connection 是在幕后创造的;是 CursorResult 返回的 execute() 调用引用 Connection 用于发出SQL语句。当 CursorResult 是关闭的,基础 Connection 为我们关闭,导致DBAPI连接返回到池,事务性资源被删除。

模式名称的翻译

要支持将公共表集分布到多个模式中的多租户应用程序,请 Connection.execution_options.schema_translate_map 执行选项可用于重新调整一组 Table 对象以不同的架构名称呈现,不做任何更改。

给出表格:

user_table = Table(
    'user', metadata_obj,
    Column('id', Integer, primary_key=True),
    Column('name', String(50))
)

这个的“模式” Table 定义如下: Table.schema 属性是 None . 这个 Connection.execution_options.schema_translate_map 可以全部指定 Table 架构为的对象 None 而是将架构呈现为 user_schema_one ::

connection = engine.connect().execution_options(
    schema_translate_map={None: "user_schema_one"})

result = connection.execute(user_table.select())

上述代码将在以下表单的数据库上调用SQL::

SELECT user_schema_one.user.id, user_schema_one.user.name FROM
user_schema_one.user

也就是说,模式名由翻译后的名称替换。映射可以指定任意数量的目标->目标架构::

connection = engine.connect().execution_options(
    schema_translate_map={
        None: "user_schema_one",     # no schema name -> "user_schema_one"
        "special": "special_schema", # schema="special" becomes "special_schema"
        "public": None               # Table objects with schema="public" will render with no schema
    })

这个 Connection.execution_options.schema_translate_map 参数影响从SQL表达式语言生成的所有DDL和SQL构造,如从 TableSequence 物体。它确实 not 影响通过 text() 构造或通过传递给 Connection.execute() .

该功能生效 only 在这些情况下,模式的名称直接从 TableSequence ;它不会影响直接传递字符串架构名称的方法。根据此模式,它在“can create”/“can drop”检查中生效,这些检查由以下方法执行 MetaData.create_all()MetaData.drop_all() 调用,并在使用表反射时生效 Table 对象。然而它确实如此 not 影响当前的操作 Inspector 对象,因为模式名被显式传递给这些方法。

小技巧

要将架构转换功能与ORM一起使用,请执行以下操作 Session 的级别设置此选项。 Engine ,然后将引擎传递给 Session 。这个 Session 使用新的 Connection 对于每笔交易::

schema_engine = engine.execution_options(schema_translate_map = { ... } )

session = Session(schema_engine)

...

1.1 新版功能.

SQL编译缓存

1.4 新版功能: 现在,在Python中,alcorm语句和SQL核心查询的转换大大降低了计算开销。请参阅 透明的SQL编译缓存添加到Core、ORM中的所有DQL、DML语句中 .

SQLAlchemy为SQL编译器及其ORM变体提供了一个全面的缓存系统。此缓存系统在 Engine 并规定给定Core或ORM SQL语句的SQL编译过程,以及为该语句装配结果获取机制的相关计算,对于该语句对象和所有其他具有相同结构的对象只会发生一次,在特定结构保留在引擎的“编译缓存”中的持续时间内。通过“具有相同结构的语句对象”,这通常对应于在函数内构造并在每次函数运行时生成的SQL语句:

def run_my_statement(connection, parameter):
    stmt = select(table)
    stmt = stmt.where(table.c.col == parameter)
    stmt = stmt.order_by(table.c.id)
    return connection.execute(stmt)

上面的语句将生成类似于 SELECT id, col FROM table WHERE col = :col ORDER BY id ,注意到 parameter 因为Python是一个纯字符串,或者它不包含字符串形式的字符串,所以它不包含字符串形式的参数。上述内容的后续调用 run_my_statement() 函数将在 connection.execute() 要求提高性能。

注解

请注意,SQL编译缓存正在缓存 只传递给数据库的SQL字符串不是数据 由查询返回。它决不是一个数据缓存,不会影响为特定SQL语句返回的结果,也不意味着任何与提取结果行相关的内存使用。

虽然SQLAlchemy从1.x系列的早期就已经有了一个基本的语句缓存,并且还为ORM提供了“bakequery”扩展,但是这两个系统都需要大量的特殊API才能使缓存有效。新的缓存从1.4版开始是完全自动的,不需要改变编程风格就可以生效。

缓存是自动使用的,不需要任何配置更改,也不需要特殊步骤来启用它。以下各节详细介绍缓存的配置和高级使用模式。

配置

缓存本身是一个类似字典的对象,称为 LRUCache 当一个特定的“最近使用的字典”和“当一个特定的关键字的内部使用量达到最小值”时,删除“最近使用的一个子类”的“alchemy”。此缓存的大小默认为500,可以使用 create_engine.query_cache_size 参数::

engine = create_engine("postgresql://scott:tiger@localhost/test", query_cache_size=1200)

在将缓存缩减为目标大小之前,缓存的大小可以增长为给定大小的150%。因此,大小为1200以上的缓存可以增长到1800个元素,此时它将被修剪为1200个。

缓存的大小基于每个引擎呈现的每个唯一SQL语句的单个条目。从内核和ORM生成的SQL语句被同等对待。DDL语句通常不会被缓存。为了确定缓存正在做什么,引擎日志记录将包括有关缓存行为的详细信息,将在下一节中介绍。

使用日志记录估计缓存性能

上面的缓存大小1200实际上相当大。对于小型应用程序,100的大小可能就足够了。要估计缓存的最佳大小,假设目标主机上有足够的内存,则缓存的大小应基于可能为正在使用的目标引擎呈现的唯一SQL字符串的数量。最方便的方法是使用SQL回显,这是通过使用 create_engine.echo 标志,或使用Python日志记录;请参阅小节 配置日志记录 有关日志记录配置的背景信息。

作为示例,我们将检查以下程序生成的日志:

from sqlalchemy import Column
from sqlalchemy import create_engine
from sqlalchemy import ForeignKey
from sqlalchemy import Integer
from sqlalchemy import String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Session

Base = declarative_base()


class A(Base):
    __tablename__ = "a"

    id = Column(Integer, primary_key=True)
    data = Column(String)
    bs = relationship("B")


class B(Base):
    __tablename__ = "b"
    id = Column(Integer, primary_key=True)
    a_id = Column(ForeignKey("a.id"))
    data = Column(String)


e = create_engine("sqlite://", echo=True)
Base.metadata.create_all(e)

s = Session(e)

s.add_all(
    [A(bs=[B(), B(), B()]), A(bs=[B(), B(), B()]), A(bs=[B(), B(), B()])]
)
s.commit()

for a_rec in s.query(A):
    print(a_rec.bs)

运行时,记录的每个SQL语句将在传递的参数左侧包含一个括号内的缓存统计标记。我们可以看到的四类信息概括如下:

  • [raw sql] -驱动程序或最终用户使用 Connection.exec_driver_sql() -缓存不适用

  • [no key] -statement对象是未缓存的DDL语句,或者statement对象包含不可缓存的元素,如用户定义的构造或任意大的值子句。

  • [generated in Xs] -声明是 缓存未命中 必须被编译,然后存储在缓存中。生成编译的构造花了X秒。数字X将以小数秒为单位。

  • [cached since Xs ago] -声明是 缓存命中 不需要重新编译。语句从X秒前就存储在缓存中。数字X将与应用程序运行的时间和语句被缓存的时间成正比,因此,例如24小时内为86400。

下面将更详细地描述每个徽章。

我们在上面的程序中看到的第一个语句将是SQLite方言检查“a”和“b”表的存在:

INFO sqlalchemy.engine.Engine PRAGMA temp.table_info("a")
INFO sqlalchemy.engine.Engine [raw sql] ()
INFO sqlalchemy.engine.Engine PRAGMA main.table_info("b")
INFO sqlalchemy.engine.Engine [raw sql] ()

对于以上两个SQLite PRAGMA语句,徽章显示 [raw sql] ,这表示驱动程序正在使用 Connection.exec_driver_sql() . 缓存不适用于这类语句,因为它们已经以字符串形式存在,而且由于SQLAlchemy没有提前解析SQL字符串,因此不知道将返回什么样的结果行。

我们看到的下一个语句是CREATE TABLE语句:

INFO sqlalchemy.engine.Engine
CREATE TABLE a (
  id INTEGER NOT NULL,
  data VARCHAR,
  PRIMARY KEY (id)
)

INFO sqlalchemy.engine.Engine [no key 0.00007s] ()
INFO sqlalchemy.engine.Engine
CREATE TABLE b (
  id INTEGER NOT NULL,
  a_id INTEGER,
  data VARCHAR,
  PRIMARY KEY (id),
  FOREIGN KEY(a_id) REFERENCES a (id)
)

INFO sqlalchemy.engine.Engine [no key 0.00006s] ()

对于这些声明,徽章上写着 [no key 0.00006s] . 这表明这两个特定的语句,缓存没有发生,因为DDL面向 CreateTable 构造未生成缓存键。DDL构造通常不参与缓存,因为它们通常不会被第二次重复,而且DDL也是数据库配置步骤,其中性能不是那么关键。

这个 [no key] 由于另一个原因,badge很重要,因为它可以为除某些当前不可缓存的特定子构造之外的可缓存SQL语句生成。这方面的示例包括不定义缓存参数的自定义用户定义SQL元素,以及一些生成任意长且不可复制的SQL字符串的构造,主要示例是 Values 在使用“多值插入”时 Insert.values() 方法。

到目前为止,我们的缓存仍然是空的。接下来的语句将被缓存,但是,段看起来像:

INFO sqlalchemy.engine.Engine INSERT INTO a (data) VALUES (?)
INFO sqlalchemy.engine.Engine [generated in 0.00011s] (None,)
INFO sqlalchemy.engine.Engine INSERT INTO a (data) VALUES (?)
INFO sqlalchemy.engine.Engine [cached since 0.0003533s ago] (None,)
INFO sqlalchemy.engine.Engine INSERT INTO a (data) VALUES (?)
INFO sqlalchemy.engine.Engine [cached since 0.0005326s ago] (None,)
INFO sqlalchemy.engine.Engine INSERT INTO b (a_id, data) VALUES (?, ?)
INFO sqlalchemy.engine.Engine [generated in 0.00010s] (1, None)
INFO sqlalchemy.engine.Engine INSERT INTO b (a_id, data) VALUES (?, ?)
INFO sqlalchemy.engine.Engine [cached since 0.0003232s ago] (1, None)
INFO sqlalchemy.engine.Engine INSERT INTO b (a_id, data) VALUES (?, ?)
INFO sqlalchemy.engine.Engine [cached since 0.0004887s ago] (1, None)

在上面,我们看到了两个基本上唯一的SQL字符串; "INSERT INTO a (data) VALUES (?)""INSERT INTO b (a_id, data) VALUES (?, ?)" . 因为SQLAlchemy对所有的文本值都使用绑定参数,即使这些语句对于不同的对象重复了很多次,因为参数是分开的,所以实际的SQL字符串保持不变。

注解

以上两个语句是由ORM工作单元流程生成的,实际上,它们将缓存在每个映射器本地的单独缓存中。然而,机械和术语是相同的。部门 禁用或使用备用字典缓存某些(或全部)语句 下面将描述面向用户的代码如何在每个语句的基础上使用备用缓存容器。

这两个语句第一次出现时,我们看到的缓存徽章是 [generated in 0.00011s] . 这表明该语句是 不在缓存中,在.00011s中编译为字符串,然后被缓存 . 当我们看到 [generated] 警徽,我们知道这意味着 缓存未命中 . 对于第一次出现的特定语句,这是预期的。但是,如果 [generated] 对于一个长时间运行的应用程序,通常会反复使用同一系列SQL语句,这可能表示 create_engine.query_cache_size 参数太小。当一个被缓存的语句由于LRU缓存修剪较少使用的项而从缓存中逐出时,它将显示 [generated] 下次使用时的徽章。

我们随后看到的这两个语句的后续出现的缓存标记如下所示 [cached since 0.0003533s ago] . 这表示语句 在缓存中找到,并且最初被放入缓存中。0003533秒前 . 需要注意的是 [generated][cached since] 徽章指的是秒数,它们的意思是不同的;在 [generated] ,该数字是编译语句所用时间的粗略计时,并且将是极少量的时间。在下列情况下 [cached since] ,这是语句在缓存中出现的总时间。这个应用程序可能已经运行了6个小时了 [cached since 21600 seconds ago] ,这是件好事。“cached since”的数字很高,这表明这些语句在很长一段时间内没有受到缓存未命中的影响。即使应用程序已经运行了很长一段时间,也经常有少量的“cached since”语句可能表明这些语句太频繁地受到缓存未命中的影响,并且 create_engine.query_cache_size 可能需要增加。

然后,我们的示例程序执行一些选择,在这里我们可以看到相同的“生成”然后“缓存”的模式,对于“a”表的选择以及“b”表的后续延迟加载:

INFO sqlalchemy.engine.Engine SELECT a.id AS a_id, a.data AS a_data
FROM a
INFO sqlalchemy.engine.Engine [generated in 0.00009s] ()
INFO sqlalchemy.engine.Engine SELECT b.id AS b_id, b.a_id AS b_a_id, b.data AS b_data
FROM b
WHERE ? = b.a_id
INFO sqlalchemy.engine.Engine [generated in 0.00010s] (1,)
INFO sqlalchemy.engine.Engine SELECT b.id AS b_id, b.a_id AS b_a_id, b.data AS b_data
FROM b
WHERE ? = b.a_id
INFO sqlalchemy.engine.Engine [cached since 0.0005922s ago] (2,)
INFO sqlalchemy.engine.Engine SELECT b.id AS b_id, b.a_id AS b_a_id, b.data AS b_data
FROM b
WHERE ? = b.a_id

在上面的程序中,完整的运行显示总共有四个不同的SQL字符串被缓存。表示缓存大小为 four 就足够了。这显然是一个非常小的大小,默认大小500可以保留为默认大小。

缓存使用多少内存?

上一节详细介绍了一些检查 create_engine.query_cache_size 需要更大一点。我们如何知道缓存是否不是太大?我们可能要设定的原因 create_engine.query_cache_size 不高于某个数字可能是因为我们有一个应用程序可能会使用大量不同的语句,例如一个从搜索UX动态构建查询的应用程序,我们不希望主机耗尽内存,例如,在过去的24小时内运行了十万个不同的查询,它们都被缓存了。

很难测量Python数据结构占用了多少内存,但是使用一个进程通过 top 当一系列连续的250条新语句被添加到缓存中时,表明一条中等大小的Core语句占用大约12K,而一条较小的ORM语句大约需要20K,其中包括对于ORM来说更大的结果获取结构。

禁用或使用备用字典缓存某些(或全部)语句

使用的内部缓存称为 LRUCache ,但这基本上只是一本字典。通过使用 Connection.execution_options.compiled_cache 选项作为执行选项。执行选项可以设置在语句上 EngineConnection ,以及在使用ORM时 Session.execute() 方法进行SQLAlchemy-2.0样式调用。例如,要运行一系列SQL语句并将它们缓存在特定的字典中:

my_cache = {}
with engine.connect().execution_options(compiled_cache=my_cache) as conn:
    conn.execute(table.select())

SQLAlchemy ORM使用上述技术来保存工作单元“刷新”进程中的每映射器缓存,这些缓存与在上配置的默认缓存分开 Engine ,以及一些关系加载器查询。

也可以使用此参数通过发送值禁用缓存 None ::

# disable caching for this connection
with engine.connect().execution_options(compiled_cache=None) as conn:
    conn.execute(table.select())

第三方方言的缓存

缓存特性要求方言的编译器在给定特定缓存键的情况下生成一般可重用的SQL结构。这意味着语句中的任何文字值,如SELECT的限制/偏移值,都不能在方言的编译方案中硬编码,因为编译后的字符串将不可重用。SQLAlChemy支持使用 BindParameter.render_literal_execute() 方法,该方法可应用于现有的 Select._limit_clauseSelect._offset_clause 属性由自定义编译器编写。

由于有许多第三方方言,其中许多方言可能会从SQL语句生成文字值,而没有利用较新的“文字执行”功能,因此SQLAlChemy从1.4.5版开始为方言添加了一个标记,称为 Dialect.supports_statement_cache 。此标志被测试为直接出现在方言类中,而不是任何超类中,因此即使是将现有的可缓存SQLAlChemy方言子类化为子类的第三方方言,如 sqlalchemy.dialects.postgresql.PGDialect 一旦根据需要更改了方言并测试了使用不同参数编译的SQL语句的可重用性,则仍必须指定此标志。

对于不支持此标志的所有第三方方言,此类方言的日志记录将指示 dialect does not support caching 。方言作者可以按如下方式应用该标志:

from sqlalchemy.engine.default import DefaultDialect

class MyDialect(DefaultDialect):
    supports_statement_cache = True

该标志还需要应用于方言的所有子类::

class MyDBAPIForMyDialect(MyDialect):
    supports_statement_cache = True

1.4.5 新版功能.

使用Lambdas为语句生成添加显著的速度增益

Deep Alchemy

这种技术通常是不必要的,除非在性能非常密集的场景中,而且是为有经验的Python程序员设计的。虽然相当简单,但它涉及到元编程概念,这些概念并不适合初级Python开发人员。lambda方法可以稍后应用到现有代码中,只需花费很少的精力。

通常表示为lambda的Python函数可用于生成可基于lambda函数本身的Python代码位置以及lambda内的闭包变量来高速缓存的SQL表达式。其基本原理是不仅允许缓存SQL表达式构造的SQL字符串编译形式(这是SQLAlChemy在不使用lambda系统时的正常行为),而且还允许缓存SQL表达式构造本身的Python内组合,这也有一定的Python开销。

lambda SQL表达式功能可以作为性能增强功能使用,也可以选择在 with_loader_criteria() orm选项,以便提供通用的SQL片段。

摘要

Lambda语句是使用 lambda_stmt() 函数,该函数返回 StatementLambdaElement ,它本身就是一个可执行语句构造。使用Python加法运算符将其他修饰符和条件添加到对象 + ,或者也可以选择 StatementLambdaElement.add_criteria() 方法,该方法允许更多选项。

假设 lambda_stmt() 构造在封闭的函数或方法中被调用,该函数或方法预计会在应用程序中多次使用,以便第一个函数或方法之外的后续执行可以利用被缓存的已编译SQL。当lambda在Python中的封闭函数内构造时,它还必须具有闭包变量,这些变量对整个方法很重要:

from sqlalchemy import lambda_stmt

def run_my_statement(connection, parameter):
    stmt = lambda_stmt(lambda: select(table))
    stmt += lambda s: s.where(table.c.col == parameter)
    stmt += lambda s: s.order_by(table.c.id)

    return connection.execute(stmt)

with engine.connect() as conn:
    result = run_my_statement(some_connection, "some parameter")

上图是三位 lambda 用于定义SELECT语句结构的可调用函数只调用一次,生成的SQL字符串缓存在引擎的编译缓存中。从那时起, run_my_statement() 函数可以被调用任意次,并且 lambda 其中的可调用函数将不会被调用,仅用作检索已编译的SQL的缓存键。

注解

需要注意的是,当不使用lambda系统时,已经有了SQL缓存。lambda系统只为通过缓存构建的SQL结构本身并使用更简单的缓存键调用的每个SQL语句添加额外的工作量减少层。

Lambdas快速指南

最重要的是,lambda SQL系统中的重点是确保为lambda生成的缓存键与它将生成的SQL字符串之间永远不会不匹配。这个 LamdaElement 相关对象将运行并分析给定的lambda,以便计算在每次运行时应该如何缓存它,试图检测任何潜在的问题。基本准则包括:

  • 支持任何类型的语句 -虽然预计 select() 构造是 lambda_stmt() 、DML语句,如 insert()update() 同样可用::

    def upd(id_, newname):
        stmt = lambda_stmt(lambda: users.update())
        stmt += lambda s: s.values(name=newname)
        stmt += lambda s: s.where(users.c.id==id_)
        return stmt
    
    with engine.begin() as conn:
        conn.execute(upd(7, "foo"))
  • 还直接支持ORM用例 -The lambda_stmt() 可以完全支持ORM功能,并可直接与一起使用 Session.execute() ::

    def select_user(session, name):
        stmt = lambda_stmt(lambda: select(User))
        stmt += lambda s: s.where(User.name == name)
    
        row = session.execute(stmt).first()
        return row
  • 自动适应绑定参数 详细说明:与SQLAlChemy以前的“烘焙查询”系统不同,lambda SQL系统支持Python文字值,这些值自动成为SQL绑定参数。这意味着即使给定的lambda只运行一次,成为绑定参数的值也是从 闭合 每次奔跑的羔羊:

    >>> def my_stmt(x, y):
    ...     stmt = lambda_stmt(lambda: select(func.max(x, y)))
    ...     return stmt
    ...
    >>> engine = create_engine("sqlite://", echo=True)
    >>> with engine.connect() as conn:
    ...     print(conn.scalar(my_stmt(5, 10)))
    ...     print(conn.scalar(my_stmt(12, 8)))
    ...
    SELECT max(?, ?) AS max_1
    [generated in 0.00057s] (5, 10)
    10
    SELECT max(?, ?) AS max_1
    [cached since 0.002059s ago] (12, 8)
    12

    上图, StatementLambdaElement 提取的值为 xy闭合 每次生成的lambda的 my_stmt() 被调用;它们作为参数值被替换到缓存的SQL结构中。

  • 理想情况下,lambda在所有情况下都应该生成相同的SQL结构 -避免在lambdas内部使用条件或自定义可调用,这可能会使其根据输入产生不同的SQL;如果一个函数可能有条件地使用两个不同的SQL片段,请使用两个单独的lambdas::

    # **Don't** do this:
    
    def my_stmt(parameter, thing=False):
        stmt = lambda_stmt(lambda: select(table))
        stmt += (
            lambda s: s.where(table.c.x > parameter) if thing
            else s.where(table.c.y == parameter)
        return stmt
    
    # **Do** do this:
    
    def my_stmt(parameter, thing=False):
        stmt = lambda_stmt(lambda: select(table))
        if thing:
            stmt += s.where(table.c.x > parameter)
        else:
            stmt += s.where(table.c.y == parameter)
        return stmt

    如果lambda不能生成一致的SQL结构,并且有些故障目前无法检测到,则可能会发生各种故障。

  • 不要在lambda中使用函数来生成绑定值 -绑定值跟踪方法要求SQL语句中要使用的实际值本地出现在lambda的闭包中。如果值是从其他函数生成的,并且 LambdaElement 如果尝试执行此操作,通常应引发错误::

    >>> def my_stmt(x, y):
    ...     def get_x():
    ...         return x
    ...     def get_y():
    ...         return y
    ...
    ...     stmt = lambda_stmt(lambda: select(func.max(get_x(), get_y())))
    ...     return stmt
    ...
    >>> with engine.connect() as conn:
    ...     print(conn.scalar(my_stmt(5, 10)))
    ...
    Traceback (most recent call last):
      # ...
    sqlalchemy.exc.InvalidRequestError: Can't invoke Python callable get_x()
    inside of lambda expression argument at
    <code object <lambda> at 0x7fed15f350e0, file "<stdin>", line 6>;
    lambda SQL constructs should not invoke functions from closure variables
    to produce literal values since the lambda SQL system normally extracts
    bound values without actually invoking the lambda or any functions within it.

    在上面,使用 get_x()get_y() ,如果它们是必要的,应该发生 外面 并分配给局部闭包变量::

    >>> def my_stmt(x, y):
    ...     def get_x():
    ...         return x
    ...     def get_y():
    ...         return y
    ...
    ...     x_param, y_param = get_x(), get_y()
    ...     stmt = lambda_stmt(lambda: select(func.max(x_param, y_param)))
    ...     return stmt
  • Avoid referring to non-SQL constructs inside of lambdas as they are not cacheable by default -此问题指的是 LambdaElement 从语句内的其他闭包变量创建缓存键。为了提供准确缓存键的最佳保证,位于lambda闭包中的所有对象都被认为是重要的,并且默认情况下将假设没有对象适合于缓存键。因此,下面的示例还将引发相当详细的错误消息:

    >>> class Foo:
    ...     def __init__(self, x, y):
    ...         self.x = x
    ...         self.y = y
    ...
    >>> def my_stmt(foo):
    ...     stmt = lambda_stmt(lambda: select(func.max(foo.x, foo.y)))
    ...     return stmt
    ...
    >>> with engine.connect() as conn:
    ...    print(conn.scalar(my_stmt(Foo(5, 10))))
    ...
    Traceback (most recent call last):
      # ...
    sqlalchemy.exc.InvalidRequestError: Closure variable named 'foo' inside of
    lambda callable <code object <lambda> at 0x7fed15f35450, file
    "<stdin>", line 2> does not refer to a cacheable SQL element, and also
    does not appear to be serving as a SQL literal bound value based on the
    default SQL expression returned by the function.  This variable needs to
    remain outside the scope of a SQL-generating lambda so that a proper cache
    key may be generated from the lambda's state.  Evaluate this variable
    outside of the lambda, set track_on=[<elements>] to explicitly select
    closure elements to track, or set track_closure_variables=False to exclude
    closure variables from being part of the cache key.

    上面的错误表明 LambdaElement 不会假设 Foo 传入的对象在所有情况下的行为都将继续相同。它也不会假设它可以使用 Foo 默认情况下作为缓存键的一部分;如果它使用 Foo 对象作为缓存键的一部分,如果有许多不同的 Foo 对象这会用重复的信息填满缓存,并且还会保存对所有这些对象的长期引用。

    解决上述情况的最佳方法是不参考 foo 在lambda内部,并引用它 外面 取而代之的是::

    >>> def my_stmt(foo):
    ...     x_param, y_param = foo.x, foo.y
    ...     stmt = lambda_stmt(lambda: select(func.max(x_param, y_param)))
    ...     return stmt

    在某些情况下,如果保证lambda的SQL结构不会根据输入更改,则传递 track_closure_variables=False 这将禁用除用于绑定参数的闭包变量之外的任何闭包变量跟踪:

    >>> def my_stmt(foo):
    ...     stmt = lambda_stmt(
    ...         lambda: select(func.max(foo.x, foo.y)),
    ...         track_closure_variables=False
    ...     )
    ...     return stmt

    还可以选择将对象添加到元素以显式形成缓存键的一部分,方法是使用 track_on 参数;使用此参数允许将特定值用作缓存键,并且还将防止考虑其他闭包变量。这对于正在构造的SQL的一部分源自可能具有许多不同值的某种上下文对象的情况很有用。在下面的示例中,SELECT语句的第一段将禁用跟踪 foo 变量,而第二个段将显式跟踪 self 作为缓存键的一部分::

    >>> def my_stmt(self, foo):
    ...     stmt = lambda_stmt(
    ...         lambda: select(*self.column_expressions),
    ...         track_closure_variables=False
    ...     )
    ...     stmt = stmt.add_criteria(
    ...         lambda: self.where_criteria,
    ...         track_on=[self]
    ...     )
    ...     return stmt

    使用 track_on 意味着给定的对象将长期存储在lambda的内部缓存中,并且只要缓存不清除这些对象(默认情况下使用包含1000个条目的LRU方案),这些对象将具有强引用。

缓存键生成

为了理解lambda SQL构造中出现的一些选项和行为,了解缓存系统是很有帮助的。

SQLAlChemy的缓存系统通常通过生成表示构造中所有状态的结构,从给定的SQL表达式构造生成缓存键:

>>> from sqlalchemy import select, column
>>> stmt = select(column('q'))
>>> cache_key = stmt._generate_cache_key()
>>> print(cache_key)  # somewhat paraphrased
CacheKey(key=(
  '0',
  <class 'sqlalchemy.sql.selectable.Select'>,
  '_raw_columns',
  (
    (
      '1',
      <class 'sqlalchemy.sql.elements.ColumnClause'>,
      'name',
      'q',
      'type',
      (
        <class 'sqlalchemy.sql.sqltypes.NullType'>,
      ),
    ),
  ),
  # a few more elements are here, and many more for a more
  # complicated SELECT statement
),)

上面的键存储在缓存中,缓存本质上是一个字典,值是一个结构,其中存储SQL语句的字符串形式,在本例中是短语“select q”。我们可以观察到,即使对于非常短的查询,缓存键也相当冗长,因为它必须表示与正在呈现和可能执行的内容可能不同的所有内容。

相比之下,lambda构造系统创建了不同类型的缓存键::

>>> from sqlalchemy import lambda_stmt
>>> stmt = lambda_stmt(lambda: select(column("q")))
>>> cache_key = stmt._generate_cache_key()
>>> print(cache_key)
CacheKey(key=(
  <code object <lambda> at 0x7fed1617c710, file "<stdin>", line 1>,
  <class 'sqlalchemy.sql.lambdas.StatementLambdaElement'>,
),)

上面,我们看到一个缓存键比非lambda语句的缓存键要短得多,而且 select(column("q")) 构造本身甚至不是必需的;Python lambda本身包含一个名为 __code__ 它指的是在应用程序的运行时内不可变且永久的Python代码对象。

当lambda还包括闭包变量时,在这些变量引用SQL结构(如列对象)的正常情况下,它们将成为缓存键的一部分,或者如果它们引用将作为绑定参数的文字值,则它们被放置在缓存键的单独元素中:

>>> def my_stmt(parameter):
...     col = column("q")
...     stmt = lambda_stmt(lambda: select(col))
...     stmt += lambda s: s.where(col == parameter)
...     return stmt

以上内容 StatementLambdaElement 包括两个lambda,这两个lambda都引用 col 关闭变量,因此缓存键将同时表示这两个段以及 column() 对象::

>>> stmt = my_stmt(5)
>>> key = stmt._generate_cache_key()
>>> print(key)
CacheKey(key=(
  <code object <lambda> at 0x7f07323c50e0, file "<stdin>", line 3>,
  (
    '0',
    <class 'sqlalchemy.sql.elements.ColumnClause'>,
    'name',
    'q',
    'type',
    (
      <class 'sqlalchemy.sql.sqltypes.NullType'>,
    ),
  ),
  <code object <lambda> at 0x7f07323c5190, file "<stdin>", line 4>,
  <class 'sqlalchemy.sql.lambdas.LinkedLambdaElement'>,
  (
    '0',
    <class 'sqlalchemy.sql.elements.ColumnClause'>,
    'name',
    'q',
    'type',
    (
      <class 'sqlalchemy.sql.sqltypes.NullType'>,
    ),
  ),
  (
    '0',
    <class 'sqlalchemy.sql.elements.ColumnClause'>,
    'name',
    'q',
    'type',
    (
      <class 'sqlalchemy.sql.sqltypes.NullType'>,
    ),
  ),
),)

缓存键的第二部分检索了调用语句时将使用的绑定参数:

>>> key.bindparams
[BindParameter('%(139668884281280 parameter)s', 5, type_=Integer())]

有关“lambda”缓存与性能比较的一系列示例,请参见 性能 性能示例。

引擎处理

这个 Engine 引用连接池,这意味着在正常情况下,存在打开的数据库连接,而 Engine 对象仍驻留在内存中。当一个 Engine 是垃圾收集的,其连接池不再由该引用 Engine ,并且假设它的所有连接都未签出,那么池及其连接也将被垃圾收集,这也会关闭实际的数据库连接。但除此之外, Engine 将保留打开的数据库连接,假定它使用 QueuePool .

这个 Engine 通常是在应用程序的整个生命周期中预先建立和维护的永久性固定装置。它是 not 目的是在每个连接的基础上创建和处理;相反,它是一个注册中心,既维护连接池,也维护数据库和DBAPI使用中的配置信息,以及每个数据库资源的某种程度的内部缓存。

但是,在许多情况下,需要 Engine 完全封闭。对于这些情况,依赖于Python垃圾收集通常不是一个好主意,而是 Engine 可以使用 Engine.dispose() 方法。这将处理引擎的底层连接池,并将其替换为空的新连接池。前提是 Engine 在此时被丢弃,不再使用 checked-in 它所指的连接也将完全关闭。

调用的有效用例 Engine.dispose() 包括:

  • 当一个程序想要释放连接池保留的任何剩余的签入连接,并且期望在将来的任何操作中不再连接到该数据库时。

  • 当程序使用多处理或 fork() 和一个 Engine 对象被复制到子进程, Engine.dispose() 应该调用,以便引擎在该分叉的本地创建全新的数据库连接。数据库连接通常是这样的 not 跨越流程边界。

  • 在测试套件或多租户场景中,许多临时的、短期的 Engine 可以创建和释放对象。

连接 签出not 当引擎被释放或垃圾收集时丢弃,因为应用程序在其他地方仍然强烈引用这些连接。然而,之后 Engine.dispose() 被调用,这些连接不再与 Engine ;当它们被关闭时,它们将被返回到它们现在的孤立连接池,一旦引用它的所有连接也不再在任何地方被引用,最终将被垃圾收集。由于这一过程不容易控制,强烈建议 Engine.dispose() 只有在签入所有已签出的连接或从其池中取消关联后才调用。

对于受 Engine 对象使用连接池是为了完全禁用池。这通常只会对新连接的使用产生适度的性能影响,这意味着当一个连接被签入时,它将完全关闭,而不会保留在内存中。见 切换池实现 有关如何禁用池的指南。

使用驱动程序SQL和原始DBAPI连接

使用简介 Connection.execute() 利用 text() 构造以说明如何调用文本SQL语句。在使用SQLAlchemy时,文本SQL实际上更像是一种例外而不是规范,因为核心表达式语言和ORM都抽象了SQL的文本表示。但是 text() 构造本身还提供了一些文本SQL的抽象,因为它规范了绑定参数的传递方式,并且支持参数和结果集行的数据类型行为。

直接向驱动程序调用SQL字符串

对于想要调用直接传递给底层驱动程序(称为 DBAPI )没有任何干涉 text() 构建 Connection.exec_driver_sql() 可使用的方法:

with engine.connect() as conn:
    conn.exec_driver_sql("SET param='bar'")

1.4 新版功能: 增加了 Connection.exec_driver_sql() 方法。

直接使用DBAPI游标

有些情况下,SQLAlchemy在访问某些 DBAPI 函数,例如调用存储过程以及处理多个结果集。在这些情况下,直接处理原始DBAPI连接也同样有利。

访问原始DBAPI连接的最常见方法是从已经存在的 Connection 直接对象。使用 Connection.connection 属性:

connection = engine.connect()
dbapi_conn = connection.connection

这里的DBAPI连接实际上是一个“代理”的原始连接池,但是这是一个在大多数情况下可以忽略的实现细节。因为DBAPI连接仍然包含在拥有的范围内 Connection 对象,最好利用 Connection 对象,用于大多数功能,如事务控制以及调用 Connection.close() 方法;如果直接在DBAPI连接上执行这些操作,则 Connection 将不知道这些状态变化。

克服由拥有者维护的DBAPI连接所施加的限制。 Connection ,DBAPI连接也可用,无需获取 Connection 首先,使用 Engine.raw_connection() 方法 Engine ::

dbapi_conn = engine.raw_connection()

这个DBAPI连接和以前一样也是一个“代理”形式。这种代理的目的现在很明显,当我们调用 .close() 此连接的方法,DBAPI连接通常不是实际关闭的,而是 released 返回引擎的连接池:

dbapi_conn.close()

尽管SQLAlchemy将来可能会为更多的DBAPI用例添加内置模式,但由于这些情况往往很少需要,并且它们在很大程度上取决于使用中的DBAPI类型,所以在任何情况下,直接DBAPI调用模式总是存在于需要的情况下。

参见

使用引擎时,如何获得原始DBAPI连接? -包括有关如何访问DBAPI连接以及使用异步驱动程序时的“驱动程序”连接的其他详细信息。

以下是DBAPI连接的一些方法。

调用存储过程和用户定义函数

SQLAlChemy支持以多种方式调用存储过程和用户定义函数。请注意,所有DBAPI都有不同的实践,因此您必须查阅底层DBAPI文档以了解与您的特定用法相关的细节。以下示例是假设的,可能不适用于您的底层DBAPI。

对于具有特殊语法或参数关注点的存储过程或函数,DBAPI级别 callproc 可能会与您的DBAPI一起使用。此模式的一个示例是:

connection = engine.raw_connection()
try:
    cursor_obj = connection.cursor()
    cursor_obj.callproc("my_procedure", ['x', 'y', 'z'])
    results = list(cursor_obj.fetchall())
    cursor_obj.close()
    connection.commit()
finally:
    connection.close()

注解

并非所有DBAPI都使用 callproc 总体使用细节会有所不同。上面的示例仅说明了使用特定DBAPI函数可能会是什么样子。

您的DBAPI可能没有 callproc 要求 or 可能需要使用另一种模式调用存储过程或用户定义函数,例如正常的SQLAlChemy连接使用。该使用模式的一个示例是, 在撰写本文档时 ,使用mental copg2 DBAPI在PostgreSQL数据库中执行存储过程,应使用正常连接用法调用该存储过程::

connection.execute("CALL my_procedure();")

上面的例子是假设的。在这些情况下,不能保证底层数据库支持“CALL”或“SELECT”,并且关键字可能会根据函数是存储过程还是用户定义函数而有所不同。在这些情况下,您应该参考底层DBAPI和数据库文档,以确定要使用的正确语法和模式。

多个结果集

多结果集支持可从原始DBAPI游标使用 nextset 方法:

connection = engine.raw_connection()
try:
    cursor_obj = connection.cursor()
    cursor_obj.execute("select * from table1; select * from table2")
    results_one = cursor_obj.fetchall()
    cursor_obj.nextset()
    results_two = cursor_obj.fetchall()
    cursor_obj.close()
finally:
    connection.close()

注册新方言

这个 create_engine() 函数调用使用SETUPTOOLS入口点定位给定方言。可以在setup.py脚本中为第三方方言建立这些入口点。例如,要创建新方言“foodialect://”,步骤如下:

  1. 创建名为 foodialect .

  2. 包应该有一个包含方言类的模块,该模块通常是 sqlalchemy.engine.default.DefaultDialect . 在这个例子中,假设它被称为 FooDialect 其模块通过 foodialect.dialect .

  3. 可以在setup.py中建立入口点,如下所示:

    entry_points="""
    [sqlalchemy.dialects]
    foodialect = foodialect.dialect:FooDialect
    """

如果方言在现有的SQLAlchemy支持的数据库之上为特定DBAPI提供支持,则可以提供名称,包括数据库限定。例如,如果 FooDialect 实际上是一个MySQL方言,入口点可以这样建立:

entry_points="""
[sqlalchemy.dialects]
mysql.foodialect = foodialect.dialect:FooDialect
"""

然后将访问上述入口点作为 create_engine("mysql+foodialect://") .

在进程中注册方言

SQLAlchemy还允许在当前进程中注册方言,而不需要单独安装。使用 register() 功能如下:

from sqlalchemy.dialects import registry
registry.register("mysql.foodialect", "myapp.dialect", "MyMySQLDialect")

以上将对 create_engine("mysql+foodialect://") 并加载 MyMySQLDialect 类从 myapp.dialect 模块。

连接/引擎API

Object NameDescription

Connection

为打包的DB-API连接提供高级功能。

CreateEnginePlugin

一组钩子,用于增强 Engine 基于URL中入口点名称的对象。

Engine

连接一个 PoolDialect 一起提供数据库连接和行为的源。

ExceptionContext

封装有关正在进行的错误条件的信息。

NestedTransaction

表示“嵌套”或保存点事务。

RootTransaction

上的“根”事务。 Connection

Transaction

表示正在进行的数据库事务。

TwoPhaseTransaction

表示两阶段事务。

class sqlalchemy.engine.Connection(engine, connection=None, close_with_result=False, _branch_from=None, _execution_options=None, _dispatch=None, _has_events=None, _allow_revalidate=True)
Object NameDescription

BaseCursorResult

数据库结果对象的基类。

ChunkedIteratorResult

IteratorResult 从迭代器产生可调用的。

CursorResult

表示DBAPI游标状态的结果。

FrozenResult

表示 Result 对象处于适合缓存的“冻结”状态。

IteratorResult

A Result 它从的Python迭代器获取数据 Row 物体。

LegacyCursorResult

的旧版本 CursorResult .

LegacyRow

一个子类 Row 它为核心提供了1.x SQLAlchemy行为。

MappingResult

包装纸 Result 返回字典值而不是 Row 价值观。

MergedResult

A Result 从任何数量的 Result 物体。

Result

表示一组数据库结果。

Row

表示单个结果行。

RowMapping

A Mapping 将列名和对象映射到 Row 价值观。

ScalarResult

包装纸 Result 返回标量值而不是 Row 价值观。

class sqlalchemy.engine.BaseCursorResult(context, cursor_strategy, cursor_description)

数据库结果对象的基类。

attribute sqlalchemy.engine.BaseCursorResult.inserted_primary_key

返回刚刚插入的行的主键。

返回值为 Row 对象,该对象按照主键列在源中的配置顺序表示主键值的命名元组 Table

在 1.4.8 版更改: -The CursorResult.inserted_primary_key 值现在是一个命名元组,通过 Row 类,而不是纯元组。

此访问器仅适用于单行 insert() 未显式指定的构造 Insert.returning() 。虽然对多行插入的支持对大多数后端都不可用,但可以使用 CursorResult.inserted_primary_key_rows 访问者。

请注意,指定server_default子句的主键列或其他列不符合“autoincrement”列的条件(请参阅中的注释 Column ,并且是使用数据库端默认值生成的,将在此列表中显示为 None 除非后端支持“返回”,并且在启用“隐式返回”的情况下执行insert语句。

加薪 InvalidRequestError 如果执行的语句不是已编译的表达式构造或不是insert()构造。

attribute sqlalchemy.engine.BaseCursorResult.inserted_primary_key_rows

返回的值 CursorResult.inserted_primary_key 作为列表中包含的一行;某些方言也可能支持多行形式。

注解

如下所示,在当前的SQLAlChemy版本中,此访问器仅在已提供的功能之外才有用 CursorResult.inserted_primary_key 在使用 PycPcG2 方言。未来的版本希望将此功能推广到更多的方言。

添加此访问器是为了支持提供当前由 Psycopg2快速执行助手 功能,当前 只有mental copg2方言 ,它允许一次插入许多行,同时仍然保留能够返回服务器生成的主键值的行为。

  • 在即将发布的版本中使用mental copg2方言或其他可能支持“Fast Executemany”样式插入的方言时 : When invoking an INSERT statement while passing a list of rows as the second argument to Connection.execute() ,则此访问器将提供行列表,其中每行都包含插入的每行的主键值。

  • When using all other dialects / backends that don't yet support this feature :此访问器仅适用于 单行INSERT语句 ,并返回与 CursorResult.inserted_primary_key 在单个元素列表中。当INSERT语句与要插入的行列表一起执行时,该列表将包含语句中插入的每行一行,但是它将包含 None 用于任何服务器生成的值。

SQLAlChemy的未来版本将进一步概括mental copg2的“快速执行帮助器”特性,以适应其他方言,从而允许该访问器具有更广泛的用途。

1.4 新版功能.

参见

CursorResult.inserted_primary_key

attribute sqlalchemy.engine.BaseCursorResult.is_insert

如果这是真的 CursorResult 是执行已编译的表达式语言的结果 insert() 构造。

如果是真的,这意味着 inserted_primary_key 属性是可访问的,假定语句不包含用户定义的“返回”构造。

method sqlalchemy.engine.BaseCursorResult.last_inserted_params()

返回此执行中插入参数的集合。

加薪 InvalidRequestError 如果执行的语句不是已编译的表达式构造或不是insert()构造。

method sqlalchemy.engine.BaseCursorResult.last_updated_params()

返回此执行中更新参数的集合。

加薪 InvalidRequestError 如果执行的语句不是已编译的表达式构造或不是update()构造。

method sqlalchemy.engine.BaseCursorResult.lastrow_has_defaults()

返回 lastrow_has_defaults() 从底层 ExecutionContext .

ExecutionContext 有关详细信息。

attribute sqlalchemy.engine.BaseCursorResult.lastrowid

返回DBAPI游标上的“lastrowid”访问器。

这是一种特定于DBAPI的方法,仅对支持它的后端和适当的语句有效。它的行为在后端不一致。

在使用insert()表达式构造时,通常不需要使用此方法;在 CursorResult.inserted_primary_key 属性为新插入的行提供主键值的元组,而不考虑数据库后端。

method sqlalchemy.engine.BaseCursorResult.postfetch_cols()

返回 postfetch_cols() 从底层 ExecutionContext .

ExecutionContext 有关详细信息。

加薪 InvalidRequestError 如果执行的语句不是已编译的表达式构造,或者不是insert()或update()构造。

method sqlalchemy.engine.BaseCursorResult.prefetch_cols()

返回 prefetch_cols() 从底层 ExecutionContext .

ExecutionContext 有关详细信息。

加薪 InvalidRequestError 如果执行的语句不是已编译的表达式构造,或者不是insert()或update()构造。

attribute sqlalchemy.engine.BaseCursorResult.returned_defaults

返回使用 ValuesBase.return_defaults() 特征。

该值是的实例 RowNone 如果 ValuesBase.return_defaults() 未使用或后端不支持返回。

0.9.0 新版功能.

参见

ValuesBase.return_defaults()

attribute sqlalchemy.engine.BaseCursorResult.returned_defaults_rows

返回一个行列表,每个行包含使用 ValuesBase.return_defaults() 特征。

返回值是 Row 物体。

1.4 新版功能.

attribute sqlalchemy.engine.BaseCursorResult.returns_rows

如果这是真的 CursorResult 返回零行或更多行。

也就是说,如果调用这些方法是合法的 CursorResult.fetchone()CursorResult.fetchmany() CursorResult.fetchall() .

总的来说 CursorResult.returns_rows 应始终与DBAPI游标是否具有 .description 属性,指示结果列的存在,注意返回零行的游标仍然具有 .description 如果发出行返回语句。

对于所有针对SELECT语句的结果以及使用RETURNING的DML语句INSERT/UPDATE/DELETE,此属性都应该为True。对于未使用RETURNING的INSERT/UPDATE/DELETE语句,该值通常为False,但是也存在一些特定于方言的异常,例如,当使用MSSQL/pyodbc方言时,为了检索插入的主键值,将内联发出SELECT。

attribute sqlalchemy.engine.BaseCursorResult.rowcount

返回此结果的“行数”。

“rowcount”报告行数 匹配的 按UPDATE或DELETE语句的WHERE条件。

注解

注意事项 CursorResult.rowcount

  • 此属性返回行数 匹配的 ,这不一定与实际的行数相同 被改进的 -例如,如果给定的设置值与行中已有的设置值相同,则UPDATE语句可能不会对给定行进行净更改。这样的行将被匹配,但不会被修改。在具有这两种样式(如mysql)的后端,默认情况下会将rowcount配置为在所有情况下返回匹配计数。

  • CursorResult.rowcountonly 与更新或删除语句一起使用。与python dbapi所说的相反,它确实做到了 not 返回select语句结果中可用的行数,因为dbapis在未缓冲行时不支持此功能。

  • CursorResult.rowcount 可能不是所有方言都能完全实现。尤其是,大多数DBAPI不支持来自ExecuteMany调用的聚合行计数结果。这个 CursorResult.supports_sane_rowcount()CursorResult.supports_sane_multi_rowcount() 如果已知支持每种用法,则方法将从方言报告。

  • 使用RETURN的语句可能不会返回正确的行数。

参见

从更新、删除获取受影响的行数 - in the SQLAlchemy 1.4/2.0教程

method sqlalchemy.engine.BaseCursorResult.supports_sane_multi_rowcount()

返回 supports_sane_multi_rowcount 来自方言。

CursorResult.rowcount 背景。

method sqlalchemy.engine.BaseCursorResult.supports_sane_rowcount()

返回 supports_sane_rowcount 来自方言。

CursorResult.rowcount 背景。

class sqlalchemy.engine.ChunkedIteratorResult(cursor_metadata, chunks, source_supports_scalars=False, raw=None, dynamic_yield_per=False)

IteratorResult 从迭代器产生可调用的。

给定的 chunks 参数是一个函数,该函数在每个块中指定要返回的行数,或者 None 对于所有行。然后函数应该返回一个未使用的列表迭代器,每个列表的大小都是请求的。

函数可以在任何时候再次调用,在这种情况下,它应该从同一个结果集继续,但是根据给定的结果调整块大小。

1.4 新版功能.

类签名

class sqlalchemy.engine.ChunkedIteratorResult (sqlalchemy.engine.IteratorResult)

method sqlalchemy.engine.ChunkedIteratorResult.yield_per(num)

配置行获取策略以一次获取num行。

当迭代result对象时,或者使用诸如 Result.fetchone() 一次返回一行。来自底层游标或其他数据源的数据将被缓冲到内存中的这么多行,然后缓冲的集合将一次产生一行或请求多少行。每次清除缓冲区时,它都将被刷新到这么多行,或者如果剩余的行数较少,则会保留相同数量的行。

这个 Result.yield_per() 方法通常与 Connection.execution_options.stream_results execution选项,如果DBAPI支持,它将允许正在使用的数据库方言使用服务器端游标。

默认情况下,大多数dbapi不使用服务器端游标,这意味着所有行都将从数据库中提前获取,而不管 Result.yield_per() 设置。然而, Result.yield_per() 它可能仍然有用,因为它对来自数据库的原始数据进行SQLAlchemy端处理,另外,当用于ORM场景时,它还可以批量将数据库行转换为ORM实体行。

1.4 新版功能.

参数

num -- 每次重新填充缓冲区时要获取的行数。如果设置为小于1的值,则获取下一个缓冲区的所有行。

class sqlalchemy.engine.FrozenResult(result)

表示 Result 对象处于适合缓存的“冻结”状态。

这个 FrozenResult 对象从 Result.freeze() 任意方法 Result 对象。

一个新的迭代器 Result 对象是从一组固定的数据中生成的,每次 FrozenResult 作为可调用的::

result = connection.execute(query)

frozen = result.freeze()

unfrozen_result_one = frozen()

for row in unfrozen_result_one:
    print(row)

unfrozen_result_two = frozen()
rows = unfrozen_result_two.all()

# ... etc

1.4 新版功能.

参见

重新执行语句 -ORM中实现结果集缓存的示例用法。

merge_frozen_result() -ORM函数将冻结的结果合并回 Session .

class sqlalchemy.engine.IteratorResult(cursor_metadata, iterator, raw=None, _source_supports_scalars=False)

A Result 它从的Python迭代器获取数据 Row 物体。

1.4 新版功能.

类签名

class sqlalchemy.engine.IteratorResult (sqlalchemy.engine.Result)

class sqlalchemy.engine.LegacyRow(parent, processors, keymap, key_style, data)

一个子类 Row 它为核心提供了1.x SQLAlchemy行为。

这个 LegacyRow 类是为row对象实现大多数Python映射(即类似字典)行为的地方。的映射行为 Row 可通过 _mapping 属性。

1.4 新版功能: -增加的 LegacyRow 它封装了 Row .

类签名

class sqlalchemy.engine.LegacyRow (sqlalchemy.engine.Row)

method sqlalchemy.engine.LegacyRow.has_key(key)

如果这个返回真 LegacyRow 包含给定的密钥。

1.4 版后已移除: 这个 LegacyRow.has_key() 方法已弃用,将在将来的版本中删除。要测试密钥成员身份,请使用 Row._mapping 属性,即'key in row.u mapping`。

通过SQLAlchemy 1.x系列 __contains__() 方法 Row (或) LegacyRow 对于SQLAlchemy 1.4),还链接到 Row.has_key() ,其中一个表达式是::

"some_col" in row

如果行包含名为的列,则将返回True "some_col" ,以Python映射的工作方式。

然而,根据计划,SQLAlchemy的2.0系列将扭转这种行为,以便 __contains__() 将以Python元组的工作方式引用行中存在的值。

参见

RowProxy不再是“代理”;现在称为Row,其行为类似于增强的命名元组

method sqlalchemy.engine.LegacyRow.items()

返回元组列表,每个元组包含一个键/值对。

1.4 版后已移除: 这个 LegacyRow.items() 方法已弃用,将在将来的版本中删除。使用 Row._mapping 属性,即'row_映射.items()'.

这个方法类似于Python字典 .items() 方法,但它返回一个列表,而不是迭代器。

method sqlalchemy.engine.LegacyRow.iterkeys()

返回一个针对 Row.keys() 方法。

1.4 版后已移除: 这个 LegacyRow.iterkeys() 方法已弃用,将在将来的版本中删除。使用 Row._mapping 属性,即'row_映射.keys()'.

此方法类似于Python-2-only字典 .iterkeys() 方法。

method sqlalchemy.engine.LegacyRow.itervalues()

返回一个针对 Row.values() 方法。

1.4 版后已移除: 这个 LegacyRow.itervalues() 方法已弃用,将在将来的版本中删除。使用 Row._mapping 属性,即'row_映射值()'.

此方法类似于Python-2-only字典 .itervalues() 方法。

method sqlalchemy.engine.LegacyRow.values()

返回 Row 作为一个列表。

1.4 版后已移除: 这个 LegacyRow.values() 方法已弃用,将在将来的版本中删除。使用 Row._mapping 属性,即'row_映射值()'.

这个方法类似于Python字典 .values() 方法,但它返回一个列表,而不是迭代器。

class sqlalchemy.engine.MergedResult(cursor_metadata, results)

A Result 从任何数量的 Result 物体。

Result.merge() 方法。

1.4 新版功能.

类签名

class sqlalchemy.engine.MergedResult (sqlalchemy.engine.IteratorResult)

class sqlalchemy.engine.Result(cursor_metadata)

表示一组数据库结果。

1.4 新版功能: 这个 Result 对象为SQLAlchemy Core和SQLAlchemy ORM提供了一个完全更新的使用模型和调用facade。在核心,它构成了 CursorResult 对象替换上一个 ResultProxy 接口。当使用ORM时,一个更高级别的对象调用 ChunkedIteratorResult 通常使用。

注解

在SQLAlChemy 1.4及更高版本中,此对象用于由返回的ORM结果 Session.execute() ,它可以单独生成ORM映射对象的实例,也可以在类似元组的行内生成ORM映射对象的实例。请注意, Result 对象不会自动对实例或行执行重复数据消除,这与旧的 Query 对象。对于实例或行的Python内重复数据消除,请使用 Result.unique() 修饰符方法。

参见

正在获取行 - in the SQLAlchemy 1.4/2.0教程

类签名

class sqlalchemy.engine.Result (sqlalchemy.engine._WithKeys, sqlalchemy.engine.ResultInternal)

method sqlalchemy.engine.Result.all()

返回列表中的所有行。

调用后关闭结果集。随后的调用将返回一个空列表。

1.4 新版功能.

返回

一览表 Row 物体。

method sqlalchemy.engine.Result.columns(*col_expressions)

建立每行中应返回的列。

此方法可用于限制返回的列以及对它们重新排序。给定的表达式列表通常是一系列整数或字符串键名。它们也可能是适当的 ColumnElement 与给定语句构造相对应的对象。

例如。::

statement = select(table.c.x, table.c.y, table.c.z)
result = connection.execute(statement)

for z, y in result.columns('z', 'y'):
    # ...

从语句本身使用列对象的示例:

for z, y in result.columns(
        statement.selected_columns.c.z,
        statement.selected_columns.c.y
):
    # ...

1.4 新版功能.

参数

*col_expressions -- 指示要返回的列。元素可以是整数行索引、字符串列名或适当的 ColumnElement 与select构造相对应的对象。

返回

Result 对象进行了修改。

method sqlalchemy.engine.Result.fetchall()

的同义词 Result.all() 方法。

method sqlalchemy.engine.Result.fetchmany(size=None)

取许多行。

当所有行都用完时,返回一个空列表。

与AlchemY.1.1兼容。

要获取组中的行,请使用 Result.partitions() 方法。

返回

一览表 Row 物体。

method sqlalchemy.engine.Result.fetchone()

取一行。

当所有行都用完时,返回None。

与AlchemY.1.1兼容。

要只获取结果的第一行,请使用 Result.first() 方法。要遍历所有行,请迭代 Result 直接对象。

返回

Row 对象(如果未应用筛选器),或者如果没有保留行,则为无。

method sqlalchemy.engine.Result.first()

获取第一行,如果没有行,则不获取。

关闭结果集并丢弃其余行。

注解

此方法返回一个 row ,例如,默认情况下是元组。要只返回一个标量值,即第一行的第一列,请使用 Result.scalar() 方法或组合 Result.scalars()Result.first() .

此外,与遗留ORM的行为形成对比的是 Query.first() 方法, 不应用任何限制 添加到调用的SQL查询,以生成此 Result ;对于在生成行之前在内存中缓冲结果的DBAPI驱动程序,所有行都将被发送到Python进程,除第一行之外的所有行都将被丢弃。

参见

与Core Select统一的ORM查询

返回

Row 对象,如果没有行保留,则无。

参见

Result.scalar()

Result.one()

method sqlalchemy.engine.Result.freeze()

返回一个可调用的对象,该对象将生成此 Result 当被调用时。

返回的可调用对象是 FrozenResult .

这用于结果集缓存。当结果尚未使用时,必须对其调用该方法,而调用该方法将完全消耗结果。当 FrozenResult 从缓存中检索时,可以多次调用它,其中它将生成一个新的 Result 对象每次针对其存储的行集。

参见

重新执行语句 -ORM中实现结果集缓存的示例用法。

method sqlalchemy.engine.Result.keys()

inherited from the sqlalchemy.engine._WithKeys.keys method of sqlalchemy.engine._WithKeys

返回一个iterable视图,该视图生成由每个键表示的字符串键 Row .

键可以表示核心语句返回的列的标签或orm执行返回的orm类的名称。

还可以使用Python测试视图的密钥包含 in 运算符,它将测试视图中表示的字符串键以及可选键(如列对象)。

在 1.4 版更改: 返回键视图对象而不是纯列表。

method sqlalchemy.engine.Result.mappings()

对返回的行应用映射筛选器,返回 MappingResult .

应用此筛选器时,获取行将返回 RowMapping 对象而不是 Row 物体。

1.4 新版功能.

返回

一个新的 MappingResult 引用此的筛选对象 Result 对象。

method sqlalchemy.engine.Result.merge(*others)

合并此 Result 与其他兼容的结果对象。

返回的对象是的实例 MergedResult 给定的迭代器将由以下对象组成。

新结果将使用此结果对象中的元数据。后续的结果对象必须针对相同的结果/游标元数据集,否则行为未定义。

method sqlalchemy.engine.Result.one()

只返回一行或引发异常。

加薪 NoResultFound 如果结果没有返回行,或者 MultipleResultsFound 如果返回多行。

注解

此方法返回一个 row ,例如,默认情况下是元组。要只返回一个标量值,即第一行的第一列,请使用 Result.scalar_one() 方法或组合 Result.scalars()Result.one() .

1.4 新版功能.

返回

第一 Row .

加薪

MultipleResultsFound, NoResultFound

参见

Result.first()

Result.one_or_none()

Result.scalar_one()

method sqlalchemy.engine.Result.one_or_none()

最多返回一个结果或引发异常。

返回 None 如果结果没有行。加薪 MultipleResultsFound 如果返回多行。

1.4 新版功能.

返回

第一 Row 如果没有行可用,则无。

加薪

MultipleResultsFound

参见

Result.first()

Result.one()

method sqlalchemy.engine.Result.partitions(size=None)

迭代给定大小的行的子列表。

每个列表的大小都是给定的,不包括要生成的最后一个列表,该列表可能只有少量的行。不会产生空列表。

当迭代器被完全使用时,result对象将自动关闭。

注意,后端驱动程序通常会提前缓冲整个结果,除非 Connection.execution_options.stream_results 如果可能,使用execution选项指示驱动程序不应预缓冲结果。并非所有驱动程序都支持此选项,对于不支持此选项的驱动程序,该选项将被忽略。

1.4 新版功能.

参数

size -- 指示生成的每个列表中要显示的最大行数。如果没有,则使用由设置的值 Result.yield_per() ,否则使用 Result.fetchmany() 可能是后端特定的默认值。

返回

列表迭代器

method sqlalchemy.engine.Result.scalar()

获取第一行的第一列,并关闭结果集。

如果没有要获取的行,则返回None。

不执行验证来测试是否还有其他行。

调用此方法后,对象将完全关闭,例如 CursorResult.close() 将调用方法。

返回

Python标量值,如果没有行保留,则为无。

method sqlalchemy.engine.Result.scalar_one()

只返回一个标量结果或引发异常。

这相当于调用 Result.scalars() 然后 Result.one()

参见

Result.one()

Result.scalars()

method sqlalchemy.engine.Result.scalar_one_or_none()

只返回一个或不返回标量结果。

这相当于调用 Result.scalars() 然后 Result.one_or_none()

参见

Result.one_or_none()

Result.scalars()

method sqlalchemy.engine.Result.scalars(index=0)

返回A ScalarResult 将返回单个元素而不是 Row 物体。

例如。::

>>> result = conn.execute(text("select int_id from table"))
>>> result.scalars().all()
[1, 2, 3]

当从 ScalarResult 筛选对象,将由 Result 而是作为列的值返回。

1.4 新版功能.

参数

index -- 整数或行键,指示要从每行提取的列,默认为 0 表示第一列。

返回

一个新的 ScalarResult 引用此的筛选对象 Result 对象。

method sqlalchemy.engine.Result.unique(strategy=None)

对此返回的对象应用唯一筛选 Result .

当不带参数应用此筛选器时,将对返回的行或对象进行筛选,以使每一行都返回唯一。默认情况下,用于确定这种唯一性的算法是整个元组的Python散列标识。在某些情况下,可以使用专门的每个实体散列方案,例如在使用ORM时,会应用一个方案,该方案针对返回对象的主键标识。

将应用唯一筛选器 在所有其他过滤器之后 ,这意味着如果已使用诸如 Result.columns()Result.scalars() 方法,将uniquing应用于 只返回一个或多个列 . 无论调用这些方法的顺序如何,都会发生这种情况 Result 对象。

独特的过滤器还改变了用于以下方法的微积分 Result.fetchmany()Result.partitions() . 使用时 Result.unique() ,在应用uniquing之后,这些方法将继续生成请求的行数或对象数。但是,这必然会影响底层游标或数据源的缓冲行为,这样多个底层调用 cursor.fetchmany() 可能需要积累足够的对象,以便提供所需大小的唯一集合。

参数

strategy -- 将应用于正在迭代的行或对象的可调用的,它应返回表示行的唯一值的对象。 Python set() 用于存储这些标识。如果未通过,则使用默认的唯一性策略,该策略可能已由此源组装 Result 对象。

method sqlalchemy.engine.Result.yield_per(num)

配置行获取策略以一次获取num行。

当迭代result对象时,或者使用诸如 Result.fetchone() 一次返回一行。来自底层游标或其他数据源的数据将被缓冲到内存中的这么多行,然后缓冲的集合将一次产生一行或请求多少行。每次清除缓冲区时,它都将被刷新到这么多行,或者如果剩余的行数较少,则会保留相同数量的行。

这个 Result.yield_per() 方法通常与 Connection.execution_options.stream_results execution选项,如果DBAPI支持,它将允许正在使用的数据库方言使用服务器端游标。

默认情况下,大多数dbapi不使用服务器端游标,这意味着所有行都将从数据库中提前获取,而不管 Result.yield_per() 设置。然而, Result.yield_per() 它可能仍然有用,因为它对来自数据库的原始数据进行SQLAlchemy端处理,另外,当用于ORM场景时,它还可以批量将数据库行转换为ORM实体行。

1.4 新版功能.

参数

num -- 每次重新填充缓冲区时要获取的行数。如果设置为小于1的值,则获取下一个缓冲区的所有行。

class sqlalchemy.engine.ScalarResult(real_result, index)

包装纸 Result 返回标量值而不是 Row 价值观。

这个 ScalarResult 对象是通过调用 Result.scalars() 方法。

对…的特殊限制 ScalarResult 它没有 fetchone() 方法;因为 fetchone() 是那个吗 None 值表示没有其他结果,这与不兼容 ScalarResult 因为没有办法区分 None 作为行值与 None 作为一个指标。使用 next(result) 单独接收值。

类签名

class sqlalchemy.engine.ScalarResult (sqlalchemy.engine.FilterResult)

method sqlalchemy.engine.ScalarResult.all()

返回列表中的所有标量值。

相当于 Result.all() 除了标量值,而不是 Row 对象,则返回。

method sqlalchemy.engine.ScalarResult.fetchall()

的同义词 ScalarResult.all() 方法。

method sqlalchemy.engine.ScalarResult.fetchmany(size=None)

获取许多对象。

相当于 Result.fetchmany() 除了标量值,而不是 Row 对象,则返回。

method sqlalchemy.engine.ScalarResult.first()

获取第一个对象,如果没有对象,则不获取。

相当于 Result.first() 除了标量值,而不是 Row 对象,则返回。

method sqlalchemy.engine.ScalarResult.one()

只返回一个对象或引发异常。

相当于 Result.one() 除了标量值,而不是 Row 对象,则返回。

method sqlalchemy.engine.ScalarResult.one_or_none()

最多返回一个对象或引发异常。

相当于 Result.one_or_none() 除了标量值,而不是 Row 对象,则返回。

method sqlalchemy.engine.ScalarResult.partitions(size=None)

迭代给定大小的元素的子列表。

相当于 Result.partitions() 除了标量值,而不是 Row 对象,则返回。

method sqlalchemy.engine.ScalarResult.unique(strategy=None)

对此返回的对象应用唯一筛选 ScalarResult .

Result.unique() 有关用法的详细信息。

class sqlalchemy.engine.MappingResult(result)

包装纸 Result 返回字典值而不是 Row 价值观。

这个 MappingResult 对象是通过调用 Result.mappings() 方法。

类签名

class sqlalchemy.engine.MappingResult (sqlalchemy.engine._WithKeys, sqlalchemy.engine.FilterResult)

method sqlalchemy.engine.MappingResult.all()

返回列表中的所有标量值。

相当于 Result.all() 除了映射值,而不是 Row 对象,则返回。

method sqlalchemy.engine.MappingResult.columns(*col_expressions)

建立每行中应返回的列。

method sqlalchemy.engine.MappingResult.fetchall()

的同义词 MappingResult.all() 方法。

method sqlalchemy.engine.MappingResult.fetchmany(size=None)

获取许多对象。

相当于 Result.fetchmany() 除了映射值,而不是 Row 对象,则返回。

method sqlalchemy.engine.MappingResult.fetchone()

取一个对象。

相当于 Result.fetchone() 除了映射值,而不是 Row 对象,则返回。

method sqlalchemy.engine.MappingResult.first()

获取第一个对象,如果没有对象,则不获取。

相当于 Result.first() 除了映射值,而不是 Row 对象,则返回。

method sqlalchemy.engine.MappingResult.keys()

inherited from the sqlalchemy.engine._WithKeys.keys method of sqlalchemy.engine._WithKeys

返回一个iterable视图,该视图生成由每个键表示的字符串键 Row .

键可以表示核心语句返回的列的标签或orm执行返回的orm类的名称。

还可以使用Python测试视图的密钥包含 in 运算符,它将测试视图中表示的字符串键以及可选键(如列对象)。

在 1.4 版更改: 返回键视图对象而不是纯列表。

method sqlalchemy.engine.MappingResult.one()

只返回一个对象或引发异常。

相当于 Result.one() 除了映射值,而不是 Row 对象,则返回。

method sqlalchemy.engine.MappingResult.one_or_none()

最多返回一个对象或引发异常。

相当于 Result.one_or_none() 除了映射值,而不是 Row 对象,则返回。

method sqlalchemy.engine.MappingResult.partitions(size=None)

迭代给定大小的元素的子列表。

相当于 Result.partitions() 除了映射值,而不是 Row 对象,则返回。

method sqlalchemy.engine.MappingResult.unique(strategy=None)

对此返回的对象应用唯一筛选 MappingResult .

Result.unique() 有关用法的详细信息。

class sqlalchemy.engine.CursorResult(context, cursor_strategy, cursor_description)

表示DBAPI游标状态的结果。

在 1.4 版更改: 这个 CursorResultLegacyCursorResult 类替换以前的 ResultProxy 接口。这些类基于 Result 调用提供更新的使用模型的API,并调用SQLAlchemy Core和SQLAlchemy ORM的facade。

通过返回数据库行 Row 类,它在DBAPI返回的原始数据的基础上提供附加的API特性和行为。通过使用诸如 Result.scalars() 方法,也可以返回其他类型的对象。

在SQLAlchemy 1.x系列的范围内,1.4版的核心SQL结果返回 LegacyCursorResult 它代替了 CursorResult 类用于1.3系列及以前版本。此对象将行返回为 LegacyRow 对象,它在对象本身上维护类似Python映射(即字典)的行为。向前看 Row._mapping 属性应用于字典行为。

参见

选择 -使用入门材料 CursorResultRow 物体。

类签名

class sqlalchemy.engine.CursorResult (sqlalchemy.engine.BaseCursorResult, sqlalchemy.engine.Result)

method sqlalchemy.engine.CursorResult.all()

inherited from the Result.all() method of Result

返回列表中的所有行。

调用后关闭结果集。随后的调用将返回一个空列表。

1.4 新版功能.

返回

一览表 Row 物体。

method sqlalchemy.engine.CursorResult.close()

关闭此 CursorResult .

这将关闭与语句执行相对应的基础DBAPI光标(如果仍存在)。注意,当 CursorResult 排出所有可用的行。 CursorResult.close() 通常是可选方法,除非在丢弃 CursorResult 还有其他行等待提取。

调用此方法后,调用将引发 ResourceClosedError 以后使用时。

参见

使用引擎和接头

method sqlalchemy.engine.CursorResult.columns(*col_expressions)

inherited from the Result.columns() method of Result

建立每行中应返回的列。

此方法可用于限制返回的列以及对它们重新排序。给定的表达式列表通常是一系列整数或字符串键名。它们也可能是适当的 ColumnElement 与给定语句构造相对应的对象。

例如。::

statement = select(table.c.x, table.c.y, table.c.z)
result = connection.execute(statement)

for z, y in result.columns('z', 'y'):
    # ...

从语句本身使用列对象的示例:

for z, y in result.columns(
        statement.selected_columns.c.z,
        statement.selected_columns.c.y
):
    # ...

1.4 新版功能.

参数

*col_expressions -- 指示要返回的列。元素可以是整数行索引、字符串列名或适当的 ColumnElement 与select构造相对应的对象。

返回

Result 对象进行了修改。

method sqlalchemy.engine.CursorResult.fetchall()

inherited from the Result.fetchall() method of Result

的同义词 Result.all() 方法。

method sqlalchemy.engine.CursorResult.fetchmany(size=None)

inherited from the Result.fetchmany() method of Result

取许多行。

当所有行都用完时,返回一个空列表。

与AlchemY.1.1兼容。

要获取组中的行,请使用 Result.partitions() 方法。

返回

一览表 Row 物体。

method sqlalchemy.engine.CursorResult.fetchone()

inherited from the Result.fetchone() method of Result

取一行。

当所有行都用完时,返回None。

与AlchemY.1.1兼容。

要只获取结果的第一行,请使用 Result.first() 方法。要遍历所有行,请迭代 Result 直接对象。

返回

Row 对象(如果未应用筛选器),或者如果没有保留行,则为无。

method sqlalchemy.engine.CursorResult.first()

inherited from the Result.first() method of Result

获取第一行,如果没有行,则不获取。

关闭结果集并丢弃其余行。

注解

此方法返回一个 row ,例如,默认情况下是元组。要只返回一个标量值,即第一行的第一列,请使用 Result.scalar() 方法或组合 Result.scalars()Result.first() .

此外,与遗留ORM的行为形成对比的是 Query.first() 方法, 不应用任何限制 添加到调用的SQL查询,以生成此 Result ;对于在生成行之前在内存中缓冲结果的DBAPI驱动程序,所有行都将被发送到Python进程,除第一行之外的所有行都将被丢弃。

参见

与Core Select统一的ORM查询

返回

Row 对象,如果没有行保留,则无。

参见

Result.scalar()

Result.one()

method sqlalchemy.engine.CursorResult.freeze()

inherited from the Result.freeze() method of Result

返回一个可调用的对象,该对象将生成此 Result 当被调用时。

返回的可调用对象是 FrozenResult .

这用于结果集缓存。当结果尚未使用时,必须对其调用该方法,而调用该方法将完全消耗结果。当 FrozenResult 从缓存中检索时,可以多次调用它,其中它将生成一个新的 Result 对象每次针对其存储的行集。

参见

重新执行语句 -ORM中实现结果集缓存的示例用法。

attribute sqlalchemy.engine.CursorResult.inserted_primary_key

inherited from the BaseCursorResult.inserted_primary_key attribute of BaseCursorResult

返回刚刚插入的行的主键。

返回值为 Row 对象,该对象按照主键列在源中的配置顺序表示主键值的命名元组 Table

在 1.4.8 版更改: -The CursorResult.inserted_primary_key 值现在是一个命名元组,通过 Row 类,而不是纯元组。

此访问器仅适用于单行 insert() 未显式指定的构造 Insert.returning() 。虽然对多行插入的支持对大多数后端都不可用,但可以使用 CursorResult.inserted_primary_key_rows 访问者。

请注意,指定server_default子句的主键列或其他列不符合“autoincrement”列的条件(请参阅中的注释 Column ,并且是使用数据库端默认值生成的,将在此列表中显示为 None 除非后端支持“返回”,并且在启用“隐式返回”的情况下执行insert语句。

加薪 InvalidRequestError 如果执行的语句不是已编译的表达式构造或不是insert()构造。

attribute sqlalchemy.engine.CursorResult.inserted_primary_key_rows

inherited from the BaseCursorResult.inserted_primary_key_rows attribute of BaseCursorResult

返回的值 CursorResult.inserted_primary_key 作为列表中包含的一行;某些方言也可能支持多行形式。

注解

如下所示,在当前的SQLAlChemy版本中,此访问器仅在已提供的功能之外才有用 CursorResult.inserted_primary_key 在使用 PycPcG2 方言。未来的版本希望将此功能推广到更多的方言。

添加此访问器是为了支持提供当前由 Psycopg2快速执行助手 功能,当前 只有mental copg2方言 ,它允许一次插入许多行,同时仍然保留能够返回服务器生成的主键值的行为。

  • 在即将发布的版本中使用mental copg2方言或其他可能支持“Fast Executemany”样式插入的方言时 : When invoking an INSERT statement while passing a list of rows as the second argument to Connection.execute() ,则此访问器将提供行列表,其中每行都包含插入的每行的主键值。

  • When using all other dialects / backends that don't yet support this feature :此访问器仅适用于 单行INSERT语句 ,并返回与 CursorResult.inserted_primary_key 在单个元素列表中。当INSERT语句与要插入的行列表一起执行时,该列表将包含语句中插入的每行一行,但是它将包含 None 用于任何服务器生成的值。

SQLAlChemy的未来版本将进一步概括mental copg2的“快速执行帮助器”特性,以适应其他方言,从而允许该访问器具有更广泛的用途。

1.4 新版功能.

参见

CursorResult.inserted_primary_key

attribute sqlalchemy.engine.CursorResult.is_insert

inherited from the BaseCursorResult.is_insert attribute of BaseCursorResult

如果这是真的 CursorResult 是执行已编译的表达式语言的结果 insert() 构造。

如果是真的,这意味着 inserted_primary_key 属性是可访问的,假定语句不包含用户定义的“返回”构造。

method sqlalchemy.engine.CursorResult.keys()

inherited from the sqlalchemy.engine._WithKeys.keys method of sqlalchemy.engine._WithKeys

返回一个iterable视图,该视图生成由每个键表示的字符串键 Row .

键可以表示核心语句返回的列的标签或orm执行返回的orm类的名称。

还可以使用Python测试视图的密钥包含 in 运算符,它将测试视图中表示的字符串键以及可选键(如列对象)。

在 1.4 版更改: 返回键视图对象而不是纯列表。

method sqlalchemy.engine.CursorResult.last_inserted_params()

inherited from the BaseCursorResult.last_inserted_params() method of BaseCursorResult

返回此执行中插入参数的集合。

加薪 InvalidRequestError 如果执行的语句不是已编译的表达式构造或不是insert()构造。

method sqlalchemy.engine.CursorResult.last_updated_params()

inherited from the BaseCursorResult.last_updated_params() method of BaseCursorResult

返回此执行中更新参数的集合。

加薪 InvalidRequestError 如果执行的语句不是已编译的表达式构造或不是update()构造。

method sqlalchemy.engine.CursorResult.lastrow_has_defaults()

inherited from the BaseCursorResult.lastrow_has_defaults() method of BaseCursorResult

返回 lastrow_has_defaults() 从底层 ExecutionContext .

ExecutionContext 有关详细信息。

attribute sqlalchemy.engine.CursorResult.lastrowid

inherited from the BaseCursorResult.lastrowid attribute of BaseCursorResult

返回DBAPI游标上的“lastrowid”访问器。

这是一种特定于DBAPI的方法,仅对支持它的后端和适当的语句有效。它的行为在后端不一致。

在使用insert()表达式构造时,通常不需要使用此方法;在 CursorResult.inserted_primary_key 属性为新插入的行提供主键值的元组,而不考虑数据库后端。

method sqlalchemy.engine.CursorResult.mappings()

inherited from the Result.mappings() method of Result

对返回的行应用映射筛选器,返回 MappingResult .

应用此筛选器时,获取行将返回 RowMapping 对象而不是 Row 物体。

1.4 新版功能.

返回

一个新的 MappingResult 引用此的筛选对象 Result 对象。

method sqlalchemy.engine.CursorResult.merge(*others)

合并此 Result 与其他兼容的结果对象。

返回的对象是的实例 MergedResult 给定的迭代器将由以下对象组成。

新结果将使用此结果对象中的元数据。后续的结果对象必须针对相同的结果/游标元数据集,否则行为未定义。

method sqlalchemy.engine.CursorResult.one()

inherited from the Result.one() method of Result

只返回一行或引发异常。

加薪 NoResultFound 如果结果没有返回行,或者 MultipleResultsFound 如果返回多行。

注解

此方法返回一个 row ,例如,默认情况下是元组。要只返回一个标量值,即第一行的第一列,请使用 Result.scalar_one() 方法或组合 Result.scalars()Result.one() .

1.4 新版功能.

返回

第一 Row .

加薪

MultipleResultsFound, NoResultFound

参见

Result.first()

Result.one_or_none()

Result.scalar_one()

method sqlalchemy.engine.CursorResult.one_or_none()

inherited from the Result.one_or_none() method of Result

最多返回一个结果或引发异常。

返回 None 如果结果没有行。加薪 MultipleResultsFound 如果返回多行。

1.4 新版功能.

返回

第一 Row 如果没有行可用,则无。

加薪

MultipleResultsFound

参见

Result.first()

Result.one()

method sqlalchemy.engine.CursorResult.partitions(size=None)

inherited from the Result.partitions() method of Result

迭代给定大小的行的子列表。

每个列表的大小都是给定的,不包括要生成的最后一个列表,该列表可能只有少量的行。不会产生空列表。

当迭代器被完全使用时,result对象将自动关闭。

注意,后端驱动程序通常会提前缓冲整个结果,除非 Connection.execution_options.stream_results 如果可能,使用execution选项指示驱动程序不应预缓冲结果。并非所有驱动程序都支持此选项,对于不支持此选项的驱动程序,该选项将被忽略。

1.4 新版功能.

参数

size -- 指示生成的每个列表中要显示的最大行数。如果没有,则使用由设置的值 Result.yield_per() ,否则使用 Result.fetchmany() 可能是后端特定的默认值。

返回

列表迭代器

method sqlalchemy.engine.CursorResult.postfetch_cols()

inherited from the BaseCursorResult.postfetch_cols() method of BaseCursorResult

返回 postfetch_cols() 从底层 ExecutionContext .

ExecutionContext 有关详细信息。

加薪 InvalidRequestError 如果执行的语句不是已编译的表达式构造,或者不是insert()或update()构造。

method sqlalchemy.engine.CursorResult.prefetch_cols()

inherited from the BaseCursorResult.prefetch_cols() method of BaseCursorResult

返回 prefetch_cols() 从底层 ExecutionContext .

ExecutionContext 有关详细信息。

加薪 InvalidRequestError 如果执行的语句不是已编译的表达式构造,或者不是insert()或update()构造。

attribute sqlalchemy.engine.CursorResult.returned_defaults

inherited from the BaseCursorResult.returned_defaults attribute of BaseCursorResult

返回使用 ValuesBase.return_defaults() 特征。

该值是的实例 RowNone 如果 ValuesBase.return_defaults() 未使用或后端不支持返回。

0.9.0 新版功能.

参见

ValuesBase.return_defaults()

attribute sqlalchemy.engine.CursorResult.returned_defaults_rows

inherited from the BaseCursorResult.returned_defaults_rows attribute of BaseCursorResult

返回一个行列表,每个行包含使用 ValuesBase.return_defaults() 特征。

返回值是 Row 物体。

1.4 新版功能.

attribute sqlalchemy.engine.CursorResult.returns_rows

inherited from the BaseCursorResult.returns_rows attribute of BaseCursorResult

如果这是真的 CursorResult 返回零行或更多行。

也就是说,如果调用这些方法是合法的 CursorResult.fetchone()CursorResult.fetchmany() CursorResult.fetchall() .

总的来说 CursorResult.returns_rows 应始终与DBAPI游标是否具有 .description 属性,指示结果列的存在,注意返回零行的游标仍然具有 .description 如果发出行返回语句。

对于所有针对SELECT语句的结果以及使用RETURNING的DML语句INSERT/UPDATE/DELETE,此属性都应该为True。对于未使用RETURNING的INSERT/UPDATE/DELETE语句,该值通常为False,但是也存在一些特定于方言的异常,例如,当使用MSSQL/pyodbc方言时,为了检索插入的主键值,将内联发出SELECT。

attribute sqlalchemy.engine.CursorResult.rowcount

inherited from the BaseCursorResult.rowcount attribute of BaseCursorResult

返回此结果的“行数”。

“rowcount”报告行数 匹配的 按UPDATE或DELETE语句的WHERE条件。

注解

注意事项 CursorResult.rowcount

  • 此属性返回行数 匹配的 ,这不一定与实际的行数相同 被改进的 -例如,如果给定的设置值与行中已有的设置值相同,则UPDATE语句可能不会对给定行进行净更改。这样的行将被匹配,但不会被修改。在具有这两种样式(如mysql)的后端,默认情况下会将rowcount配置为在所有情况下返回匹配计数。

  • CursorResult.rowcountonly 与更新或删除语句一起使用。与python dbapi所说的相反,它确实做到了 not 返回select语句结果中可用的行数,因为dbapis在未缓冲行时不支持此功能。

  • CursorResult.rowcount 可能不是所有方言都能完全实现。尤其是,大多数DBAPI不支持来自ExecuteMany调用的聚合行计数结果。这个 CursorResult.supports_sane_rowcount()CursorResult.supports_sane_multi_rowcount() 如果已知支持每种用法,则方法将从方言报告。

  • 使用RETURN的语句可能不会返回正确的行数。

参见

从更新、删除获取受影响的行数 - in the SQLAlchemy 1.4/2.0教程

method sqlalchemy.engine.CursorResult.scalar()

inherited from the Result.scalar() method of Result

获取第一行的第一列,并关闭结果集。

如果没有要获取的行,则返回None。

不执行验证来测试是否还有其他行。

调用此方法后,对象将完全关闭,例如 CursorResult.close() 将调用方法。

返回

Python标量值,如果没有行保留,则为无。

method sqlalchemy.engine.CursorResult.scalar_one()

inherited from the Result.scalar_one() method of Result

只返回一个标量结果或引发异常。

这相当于调用 Result.scalars() 然后 Result.one()

参见

Result.one()

Result.scalars()

method sqlalchemy.engine.CursorResult.scalar_one_or_none()

inherited from the Result.scalar_one_or_none() method of Result

只返回一个或不返回标量结果。

这相当于调用 Result.scalars() 然后 Result.one_or_none()

参见

Result.one_or_none()

Result.scalars()

method sqlalchemy.engine.CursorResult.scalars(index=0)

inherited from the Result.scalars() method of Result

返回A ScalarResult 将返回单个元素而不是 Row 物体。

例如。::

>>> result = conn.execute(text("select int_id from table"))
>>> result.scalars().all()
[1, 2, 3]

当从 ScalarResult 筛选对象,将由 Result 而是作为列的值返回。

1.4 新版功能.

参数

index -- 整数或行键,指示要从每行提取的列,默认为 0 表示第一列。

返回

一个新的 ScalarResult 引用此的筛选对象 Result 对象。

method sqlalchemy.engine.CursorResult.supports_sane_multi_rowcount()

inherited from the BaseCursorResult.supports_sane_multi_rowcount() method of BaseCursorResult

返回 supports_sane_multi_rowcount 来自方言。

CursorResult.rowcount 背景。

method sqlalchemy.engine.CursorResult.supports_sane_rowcount()

inherited from the BaseCursorResult.supports_sane_rowcount() method of BaseCursorResult

返回 supports_sane_rowcount 来自方言。

CursorResult.rowcount 背景。

method sqlalchemy.engine.CursorResult.unique(strategy=None)

inherited from the Result.unique() method of Result

对此返回的对象应用唯一筛选 Result .

当不带参数应用此筛选器时,将对返回的行或对象进行筛选,以使每一行都返回唯一。默认情况下,用于确定这种唯一性的算法是整个元组的Python散列标识。在某些情况下,可以使用专门的每个实体散列方案,例如在使用ORM时,会应用一个方案,该方案针对返回对象的主键标识。

将应用唯一筛选器 在所有其他过滤器之后 ,这意味着如果已使用诸如 Result.columns()Result.scalars() 方法,将uniquing应用于 只返回一个或多个列 . 无论调用这些方法的顺序如何,都会发生这种情况 Result 对象。

独特的过滤器还改变了用于以下方法的微积分 Result.fetchmany()Result.partitions() . 使用时 Result.unique() ,在应用uniquing之后,这些方法将继续生成请求的行数或对象数。但是,这必然会影响底层游标或数据源的缓冲行为,这样多个底层调用 cursor.fetchmany() 可能需要积累足够的对象,以便提供所需大小的唯一集合。

参数

strategy -- 将应用于正在迭代的行或对象的可调用的,它应返回表示行的唯一值的对象。 Python set() 用于存储这些标识。如果未通过,则使用默认的唯一性策略,该策略可能已由此源组装 Result 对象。

method sqlalchemy.engine.CursorResult.yield_per(num)

配置行获取策略以一次获取num行。

当迭代result对象时,或者使用诸如 Result.fetchone() 一次返回一行。来自底层游标或其他数据源的数据将被缓冲到内存中的这么多行,然后缓冲的集合将一次产生一行或请求多少行。每次清除缓冲区时,它都将被刷新到这么多行,或者如果剩余的行数较少,则会保留相同数量的行。

这个 Result.yield_per() 方法通常与 Connection.execution_options.stream_results execution选项,如果DBAPI支持,它将允许正在使用的数据库方言使用服务器端游标。

默认情况下,大多数dbapi不使用服务器端游标,这意味着所有行都将从数据库中提前获取,而不管 Result.yield_per() 设置。然而, Result.yield_per() 它可能仍然有用,因为它对来自数据库的原始数据进行SQLAlchemy端处理,另外,当用于ORM场景时,它还可以批量将数据库行转换为ORM实体行。

1.4 新版功能.

参数

num -- 每次重新填充缓冲区时要获取的行数。如果设置为小于1的值,则获取下一个缓冲区的所有行。

class sqlalchemy.engine.LegacyCursorResult(context, cursor_strategy, cursor_description)

的旧版本 CursorResult .

此类包括用于“无连接”执行的连接“连接自动关闭”行为,以及使用 LegacyRow 行实现。

1.4 新版功能.

类签名

class sqlalchemy.engine.LegacyCursorResult (sqlalchemy.engine.CursorResult)

method sqlalchemy.engine.LegacyCursorResult.close()

关闭此 LegacyCursorResult .

此方法的行为与的相同 sqlalchemy.engine.CursorResult() 但它也可能关闭潜在的 Connection 对于“无连接”执行的情况。

2.0 版后已移除: “无连接”执行已弃用,将在版本2.0中删除。版本2.0将具有 Result 对象,该对象在任何情况下都不会再影响原始连接的状态。

调用此方法后,调用将引发 ResourceClosedError 以后使用时。

参见

使用引擎和接头

无连接执行,隐式执行

class sqlalchemy.engine.Row(parent, processors, keymap, key_style, data)

表示单个结果行。

这个 Row 对象表示数据库结果的一行。它通常在SQLAlchemy的1.x系列中与 CursorResult 对象,但是,从SQLAlchemy 1.4开始,ORM也将其用于类似元组的结果。

这个 Row 对象的作用尽可能类似于名为tuple的Python。对于映射(即字典)行上的行为,例如测试键的包含,请参阅 Row._mapping 属性。

参见

选择 -包括从SELECT语句中选择行的示例。

LegacyRow -SQLAlchemy 1.4中引入的兼容性接口。

在 1.4 版更改: 更名 RowProxyRow . Row 不再是一个“代理”对象,因为它包含最终形式的数据,现在的行为基本上类似于命名元组。类似映射的功能被移动到 Row._mapping 属性,但将通过 LegacyRow 由使用的类 LegacyCursorResult . 见 RowProxy不再是“代理”;现在称为Row,其行为类似于增强的命名元组 关于这个变化的背景。

类签名

class sqlalchemy.engine.Row (sqlalchemy.engine.BaseRow, collections.abc.Sequence)

method sqlalchemy.engine.Row._asdict()

返回将字段名映射到其相应值的新字典。

此方法类似于名为tuple的Python ._asdict() 方法,并通过应用 dict() 构造函数添加到 Row._mapping 属性。

1.4 新版功能.

参见

Row._mapping

attribute sqlalchemy.engine.Row._fields

返回字符串键的元组,如 Row .

键可以表示核心语句返回的列的标签或orm执行返回的orm类的名称。

这个属性类似于名为tuple的Python ._fields 属性。

1.4 新版功能.

参见

Row._mapping

attribute sqlalchemy.engine.Row._mapping

返回A RowMapping 为此 Row .

此对象为行中包含的数据提供一致的Python映射(即dictionary)接口。这个 Row 它本身的行为类似于命名元组,但是在SQLAlchemy的1.4系列中 LegacyRow 类仍然由Core使用,它继续对row对象本身有类似映射的行为。

参见

Row._fields

1.4 新版功能.

attribute sqlalchemy.engine.Row.count
attribute sqlalchemy.engine.Row.index
method sqlalchemy.engine.Row.keys()

以字符串形式返回键列表 Row .

1.4 版后已移除: 这个 Row.keys() 方法在SQLAlchemy的1.x系列中被认为是遗留的,并将在2.0中删除。使用namedtuple标准访问器 Row._fields ,或对于完全映射行为,请使用row_映射.keys()(SQLAlchemy 2.0的背景: 迁移到Alchemy

键可以表示核心语句返回的列的标签或orm执行返回的orm类的名称。

这个方法类似于Python字典 .keys() 方法,但它返回一个列表,而不是迭代器。

参见

Row._fields

Row._mapping

class sqlalchemy.engine.RowMapping(parent, processors, keymap, key_style, data)

A Mapping 将列名和对象映射到 Row 价值观。

这个 RowMapping 可从 Row 通过 Row._mapping 属性提供的iterable接口 MappingResult 返回的对象 Result.mappings() 方法。

RowMapping 提供对行内容的Python映射(即字典)访问。这包括支持测试特定键(字符串列名或对象)的包含,以及键、值和项的迭代:

for row in result:
    if 'a' in row._mapping:
        print("Column 'a': %s" % row._mapping['a'])

    print("Column b: %s" % row._mapping[table.c.b])

1.4 新版功能: 这个 RowMapping 对象替换以前由数据库结果行提供的类似于映射的访问,现在该行的行为主要类似于命名元组。

类签名

class sqlalchemy.engine.RowMapping (sqlalchemy.engine.BaseRow, collections.abc.Mapping)

method sqlalchemy.engine.RowMapping.items()

返回底层元素的键/值元组的视图 Row .

method sqlalchemy.engine.RowMapping.keys()

返回由底层表示的字符串列名的“keys”视图 Row .

method sqlalchemy.engine.RowMapping.values()

返回底层中表示的值的值的视图 Row .