SqlAlchemy 2.0 中文文档(十七)(1)https://developer.aliyun.com/article/1562952
禁用对具有多个参数集的 UPDATE 语句进行按主键的 ORM 批量更新
当:
- 给出的 UPDATE 语句针对 ORM 实体
Session
用于执行语句,而不是核心Connection
- 传递的参数是字典列表。
为了调用不使用“按主键的 ORM 批量更新”的 UPDATE 语句,直接使用 Session.connection()
方法对当前事务获取 Connection
执行语句:
>>> from sqlalchemy import bindparam >>> session.connection().execute( ... update(User).where(User.name == bindparam("u_name")), ... [ ... {"u_name": "spongebob", "fullname": "Spongebob Squarepants"}, ... {"u_name": "patrick", "fullname": "Patrick Star"}, ... ], ... ) UPDATE user_account SET fullname=? WHERE user_account.name = ? [...] [('Spongebob Squarepants', 'spongebob'), ('Patrick Star', 'patrick')] <...>
另请参阅
按主键每行的 ORM 批量更新要求记录包含主键值 ### 联合表继承的按主键批量更新
当使用具有联合表继承的映射时,ORM 批量更新的行为与使用映射进行批量插入时类似;如 联合表继承的批量插入 中所述,批量更新操作将为映射中表示的每个表发出一条 UPDATE 语句,其中给定的参数包括要更新的值(不受影响的表将被跳过)。
示例:
>>> session.execute( ... update(Manager), ... [ ... { ... "id": 1, ... "name": "scheeks", ... "manager_name": "Sandy Cheeks, President", ... }, ... { ... "id": 2, ... "name": "eugene", ... "manager_name": "Eugene H. Krabs, VP Marketing", ... }, ... ], ... ) UPDATE employee SET name=? WHERE employee.id = ? [...] [('scheeks', 1), ('eugene', 2)] UPDATE manager SET manager_name=? WHERE manager.id = ? [...] [('Sandy Cheeks, President', 1), ('Eugene H. Krabs, VP Marketing', 2)] <...> ```### 旧版会话批量更新方法 如传统会话批量 INSERT 方法所讨论的,`Session.bulk_update_mappings()`方法是批量更新的传统形式,当解释具有给定主键参数的`update()`语句时,ORM 在内部使用它;但是,当使用传统版本时,诸如会话同步支持之类的功能是不包括的。 下面的示例: ```py session.bulk_update_mappings( User, [ {"id": 1, "name": "scheeks", "manager_name": "Sandy Cheeks, President"}, {"id": 2, "name": "eugene", "manager_name": "Eugene H. Krabs, VP Marketing"}, ], )
使用新 API 表示为:
from sqlalchemy import update session.execute( update(User), [ {"id": 1, "name": "scheeks", "manager_name": "Sandy Cheeks, President"}, {"id": 2, "name": "eugene", "manager_name": "Eugene H. Krabs, VP Marketing"}, ], )
请参见
传统会话批量 INSERT 方法 ## 使用自定义 WHERE 条件的 ORM UPDATE 和 DELETE
当使用自定义 WHERE 条件构造Update
和Delete
构造时(即使用Update.where()
和Delete.where()
方法),可以通过将它们传递给Session.execute()
在 ORM 上下文中调用,而不使用Session.execute.params
参数。对于Update
,要更新的值应该使用Update.values()
传递。
此使用模式与之前描述的功能不同 ORM 按主键批量更新,ORM 使用给定的 WHERE 子句,而不是将 WHERE 子句固定为主键。这意味着单个 UPDATE 或 DELETE 语句可以一次性影响许多行。
举例来说,下面发出一个 UPDATE,影响多行的“fullname”字段
>>> from sqlalchemy import update >>> stmt = ( ... update(User) ... .where(User.name.in_(["squidward", "sandy"])) ... .values(fullname="Name starts with S") ... ) >>> session.execute(stmt) UPDATE user_account SET fullname=? WHERE user_account.name IN (?, ?) [...] ('Name starts with S', 'squidward', 'sandy') <...>
对于 DELETE,基于条件删除行的示例:
>>> from sqlalchemy import delete >>> stmt = delete(User).where(User.name.in_(["squidward", "sandy"])) >>> session.execute(stmt) DELETE FROM user_account WHERE user_account.name IN (?, ?) [...] ('squidward', 'sandy') <...>
警告
请阅读以下部分 ORM 启用更新和删除的重要注意事项和注意事项,以了解 ORM 启用的 UPDATE 和 DELETE 功能与 ORM 工作单元 功能的功能不同,例如使用Session.delete()
方法删除单个对象。
ORM-启用的 Update 和 Delete 的重要说明和注意事项
ORM 启用的 UPDATE 和 DELETE 功能绕过 ORM 工作单元 自动化,以便能够发出一条匹配多行的 UPDATE 或 DELETE 语句,而不会复杂化。
- 操作不提供 Python 中的关系级联功能 - 假定任何需要的外键引用都已配置为 ON UPDATE CASCADE 和/或 ON DELETE CASCADE,否则如果强制执行外键引用,则数据库可能会发出完整性违规。有关一些示例,请参阅使用外键 ON DELETE cascade 与 ORM 关系的注意事项。
- 在 UPDATE 或 DELETE 之后,
Session
中受到影响的依赖对象,特别是那些引用现在已被删除的行的 ON UPDATE CASCADE 或 ON DELETE CASCADE 的相关表的对象,可能仍然引用这些对象。此问题在Session
过期时解决,通常发生在Session.commit()
时或可以通过使用Session.expire_all()
强制执行。 - 启用 ORM 的 UPDATE 和 DELETE 不会自动处理连接的表继承。有关如何处理连接继承映射的说明,请参阅具有自定义 WHERE 条件的连接表继承的 UPDATE/DELETE 部分。
- 为了将单表继承映射的多态标识限制为特定子类所需的 WHERE 条件会自动包含。这仅适用于没有自己表的子类映射器。
- ORM 更新和删除操作支持
with_loader_criteria()
选项;此处的条件将被添加到正在发出的 UPDATE 或 DELETE 语句的条件中,并在“同步”过程中考虑。 - 要拦截启用 ORM 的 UPDATE 和 DELETE 操作以使用事件处理程序,请使用
SessionEvents.do_orm_execute()
事件。 ### 选择同步策略
在使用update()
或delete()
与启用 ORM 执行一起使用Session.execute()
时,将存在额外的 ORM 特定功能,该功能将同步语句更改的状态与当前存在于Session
的 identity map 中的对象的状态。通过“同步”,我们指的是更新的属性将使用新值刷新,或者至少过期,以便在下次访问时重新填充其新值,并且删除的对象将移至 deleted 状态。
此同步可通过“同步策略”控制,该策略作为字符串 ORM 执行选项传递,通常使用Session.execute.execution_options
字典:
>>> from sqlalchemy import update >>> stmt = ( ... update(User).where(User.name == "squidward").values(fullname="Squidward Tentacles") ... ) >>> session.execute(stmt, execution_options={"synchronize_session": False}) UPDATE user_account SET fullname=? WHERE user_account.name = ? [...] ('Squidward Tentacles', 'squidward') <...>
执行选项也可以与语句本身捆绑在一起,使用Executable.execution_options()
方法:
>>> from sqlalchemy import update >>> stmt = ( ... update(User) ... .where(User.name == "squidward") ... .values(fullname="Squidward Tentacles") ... .execution_options(synchronize_session=False) ... ) >>> session.execute(stmt) UPDATE user_account SET fullname=? WHERE user_account.name = ? [...] ('Squidward Tentacles', 'squidward') <...>
支持以下synchronize_session
的值:
'auto'
- 这是默认值。在支持 RETURNING 的后端上将使用'fetch'
策略,这包括除 MySQL 外的所有 SQLAlchemy 本机驱动程序。如果不支持 RETURNING,则将改为使用'evaluate'
策略。'fetch'
- 通过在执行 UPDATE 或 DELETE 之前执行 SELECT 或使用 RETURNING(如果数据库支持)来检索受影响行的主键标识,以便受操作影响的内存对象可以使用新值刷新(更新)或从Session
中删除(删除)。即使给定的update()
或delete()
构造明确指定实体或列使用UpdateBase.returning()
,也可以使用此同步策略。
2.0 版中的更改:在使用启用 ORM 的 UPDATE 和 DELETE 以 WHERE 条件时,可以将明确的UpdateBase.returning()
与'fetch'
同步策略结合使用。实际语句将包含'fetch'
策略所需的列和请求的列之间的并集。'evaluate'
- 这表示在 Python 中评估 UPDATE 或 DELETE 语句中给定的 WHERE 条件,以定位Session
中的匹配对象。这种方法不会为操作添加任何 SQL 往返,并且在没有 RETURNING 支持的情况下,可能更有效。对于具有复杂条件的 UPDATE 或 DELETE 语句,'evaluate'
策略可能无法在 Python 中评估表达式,并且会引发错误。如果发生这种情况,请改用该操作的'fetch'
策略。
提示
如果 SQL 表达式使用Operators.op()
或custom_op
功能使用自定义运算符,则可以使用Operators.op.python_impl
参数指示将由"evaluate"
同步策略使用的 Python 函数。
2.0 版中的新功能。
警告
如果要在具有许多已过期对象的Session
上运行 UPDATE 操作,则应避免使用"evaluate"
策略,因为它将必须刷新对象以便根据给定的 WHERE 条件测试它们,这将为每个对象发出一个 SELECT。在这种情况下,特别是如果后端支持 RETURNING,则应优先选择"fetch"
策略。False
- 不同步会话。该选项对于不支持 RETURNING 的后端可能很有用,其中无法使用"evaluate"
策略。在这种情况下,Session
中对象的状态不变,不会自动对应于发出的 UPDATE 或 DELETE 语句,如果存在通常与匹配行对应的对象。### 使用 UPDATE/DELETE 和自定义 WHERE 条件的 RETURNING
UpdateBase.returning()
方法与启用 ORM 的 UPDATE 和 DELETE 以 WHERE 条件完全兼容。完整的 ORM 对象和/或列可以用于 RETURNING:
>>> from sqlalchemy import update >>> stmt = ( ... update(User) ... .where(User.name == "squidward") ... .values(fullname="Squidward Tentacles") ... .returning(User) ... ) >>> result = session.scalars(stmt) UPDATE user_account SET fullname=? WHERE user_account.name = ? RETURNING id, name, fullname, species [...] ('Squidward Tentacles', 'squidward') >>> print(result.all()) [User(name='squidward', fullname='Squidward Tentacles')]
RETURNING 的支持也与 fetch
同步策略兼容,fetch
同样使用 RETURNING。ORM 将适当地组织 RETURNING 中的列,以便同步进程顺利进行,并且返回的 Result
将以请求的实体和 SQL 列的请求顺序包含。
2.0 版中的新功能:UpdateBase.returning()
可用于启用 ORM 的 UPDATE 和 DELETE,并仍保留与 fetch
同步策略的完全兼容性。### 使用自定义 WHERE 条件进行连接表继承的 UPDATE/DELETE
带有 WHERE 条件的 UPDATE/DELETE 功能,不像 基于主键的 ORM 大规模 UPDATE,每次调用 Session.execute()
时只发出单个 UPDATE 或 DELETE 语句。这意味着当针对多表映射运行 update()
或 delete()
语句时,如连接表继承映射中的子类,该语句必须符合后端当前的功能,这可能包括后端不支持引用多个表的 UPDATE 或 DELETE 语句,或者仅对此有限支持。这意味着对于诸如连接继承子类之类的映射,UPDATE/DELETE 功能的 ORM 版本只能在有限程度上使用或根本无法使用,具体取决于具体情况。
对于连接表子类发出多行 UPDATE 语句的最直接方法是仅引用子表。这意味着 Update()
构造应仅引用本地于子类表的属性,如下例所示:
>>> stmt = ( ... update(Manager) ... .where(Manager.id == 1) ... .values(manager_name="Sandy Cheeks, President") ... ) >>> session.execute(stmt) UPDATE manager SET manager_name=? WHERE manager.id = ? [...] ('Sandy Cheeks, President', 1) <...>
使用上述形式,一个简单的方法是引用基表来定位任何 SQL 后端上的行,可以使用子查询:
>>> stmt = ( ... update(Manager) ... .where( ... Manager.id ... == select(Employee.id).where(Employee.name == "sandy").scalar_subquery() ... ) ... .values(manager_name="Sandy Cheeks, President") ... ) >>> session.execute(stmt) UPDATE manager SET manager_name=? WHERE manager.id = (SELECT employee.id FROM employee WHERE employee.name = ?) RETURNING id [...] ('Sandy Cheeks, President', 'sandy') <...>
对于支持 UPDATE…FROM 的后端,子查询可以作为额外的普通 WHERE 条件陈述,但是两个表之间的条件必须以某种方式明确陈述:
>>> stmt = ( ... update(Manager) ... .where(Manager.id == Employee.id, Employee.name == "sandy") ... .values(manager_name="Sandy Cheeks, President") ... ) >>> session.execute(stmt) UPDATE manager SET manager_name=? FROM employee WHERE manager.id = employee.id AND employee.name = ? [...] ('Sandy Cheeks, President', 'sandy') <...>
对于 DELETE 操作,预期基表和子表中的行将同时被 DELETE。要在不使用级联外键的情况下 DELETE 多行连接继承对象,应分别发出针对每个表的 DELETE:
>>> from sqlalchemy import delete >>> session.execute(delete(Manager).where(Manager.id == 1)) DELETE FROM manager WHERE manager.id = ? [...] (1,) <...> >>> session.execute(delete(Employee).where(Employee.id == 1)) DELETE FROM employee WHERE employee.id = ? [...] (1,) <...>
总的来说,对于更新和删除联合继承和其他多表映射的行,应优先使用普通的工作单元流程,除非存在使用自定义 WHERE 条件的性能原因。
旧版查询方法
原始的 ORM 启用的带有 WHERE 功能的 UPDATE/DELETE 最初是 Query
对象的一部分,位于 Query.update()
和 Query.delete()
方法中。这些方法仍然可用,并提供与 ORM UPDATE and DELETE with Custom WHERE Criteria 描述的部分相同的功能。主要区别在于旧版方法不提供显式的 RETURNING 支持。
请参阅
Query.update()
Query.delete()
## ORM 批量插入语句
可以基于 ORM 类构建 insert()
构造,并将其传递给 Session.execute()
方法。发送到 Session.execute.params
参数的参数字典列表,与 Insert
对象本身分开,将为语句调用批量插入模式,这基本上意味着操作将尽可能地为许多行进行优化:
>>> from sqlalchemy import insert >>> session.execute( ... insert(User), ... [ ... {"name": "spongebob", "fullname": "Spongebob Squarepants"}, ... {"name": "sandy", "fullname": "Sandy Cheeks"}, ... {"name": "patrick", "fullname": "Patrick Star"}, ... {"name": "squidward", "fullname": "Squidward Tentacles"}, ... {"name": "ehkrabs", "fullname": "Eugene H. Krabs"}, ... ], ... ) INSERT INTO user_account (name, fullname) VALUES (?, ?) [...] [('spongebob', 'Spongebob Squarepants'), ('sandy', 'Sandy Cheeks'), ('patrick', 'Patrick Star'), ('squidward', 'Squidward Tentacles'), ('ehkrabs', 'Eugene H. Krabs')] <...>
参数字典包含键值对,这些键值对可能对应于与映射的 Column
或 mapped_column()
声明相对应的 ORM 映射属性,以及与组合声明相对应的映射。如果这两个名称恰好不同,则键应与ORM 映射属性名称匹配,而不是实际的数据库列名称。
2.0 版本中的更改:将 Insert
结构传递给 Session.execute()
方法现在会调用“批量插入”,这利用了与传统的 Session.bulk_insert_mappings()
方法相同的功能。这与 1.x 系列中的行为变化相比,1.x 系列中 Insert
将以核心为中心的方式解释,使用列名作为值键;现在接受 ORM 属性键。通过将执行选项 {"dml_strategy": "raw"}
传递给 Session.execute()
方法的 Session.execution_options
参数,可以使用核心样式功能。
SqlAlchemy 2.0 中文文档(十七)(3)https://developer.aliyun.com/article/1562954