支持 ORM 的 INSERT、UPDATE 和 DELETE 语句¶
Session.execute()
方法,除了处理启用了 ORM 的
Select
对象,也可以容纳启用 ORM 的对象
Insert
、Update
和 Delete
对象,每种方式都用于一次 INSERT、UPDATE 或 DELETE 多个数据库行。对启用 ORM 的“upserts”也有特定于方言的支持,这些 UPSERTS 是 INSERT 语句,可自动对已经存在的行使用 UPDATE。
下表总结了本文档中讨论的呼叫形式:
|
|
|
|
|
---|---|---|---|---|
|
||||
|
|
|||
|
|
|||
|
|
|||
|
ORM 批量 INSERT 语句¶
insert()
结构可以根据 ORM 类构造并传递给 Session.execute()
方法。发送到 Session.execute.params
参数的参数字典列表,与 Insert
对象本身分开,将调用批量 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')]
<...>
参数字典包含键/值对,这些键/值对可能对应于与 mapped Column
对齐的 ORM 映射属性
或 mapped_column()
声明,以及使用
复合声明。如果这两个名称恰好不同,则键应与 ORM 映射的属性名称匹配,而不是与实际的数据库列名称匹配。
在 2.0 版更改: 将 Insert
构造传递给
Session.execute()
方法现在调用一个 “bulk insert”,它
使用与旧版
Session.bulk_insert_mappings()
方法。与 1.x 系列相比,这是一个行为更改,在 1.x 系列中,Insert
将以 Core 为中心的方式解释,使用值键的列名;现在接受 ORM 属性键。通过将执行选项 {“dml_strategy”: “raw”}
传递给
Session.execution_options
参数
Session.execute() 的 Session.execute()
中。
使用 RETURNING 获取新对象¶
批量 ORM 插入功能支持 INSERT..RETURNING 对于选定的后端,它可以返回一个 Result
对象,该对象可能会返回单个列以及与新生成的记录相对应的完全构建的 ORM 对象。插入。。RETURNING 需要使用支持 SQL RETURNING 语法的后端,并支持 executemany
与 RETURNING;此功能在所有
包含 SQLAlchemy 的后端,但 MySQL 除外(包括 MariaDB)。
例如,我们可以运行与之前相同的语句,并在
UpdateBase.returning()
方法,将完整的 User
实体作为我们想要返回的实体传递。Session.scalars()
用于允许迭代 User
对象:
>>> users = session.scalars(
... insert(User).returning(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 (?, ?), (?, ?), (?, ?), (?, ?), (?, ?)
RETURNING id, name, fullname, species
[...] ('spongebob', 'Spongebob Squarepants', 'sandy', 'Sandy Cheeks',
'patrick', 'Patrick Star', 'squidward', 'Squidward Tentacles',
'ehkrabs', 'Eugene H. Krabs')
>>> print(users.all())
[User(name='spongebob', fullname='Spongebob Squarepants'),
User(name='sandy', fullname='Sandy Cheeks'),
User(name='patrick', fullname='Patrick Star'),
User(name='squidward', fullname='Squidward Tentacles'),
User(name='ehkrabs', fullname='Eugene H. Krabs')]
在上面的示例中,渲染的 SQL 采用
insertmanyvalues 功能,其中各个参数字典内联到单个 INSERT 语句中,以便可以使用 RETURNING。
在 2.0 版更改: ORM Session
现在在 ORM 上下文中解释 Insert
、Update
甚至 Delete
结构中的 RETURNING 子句,这意味着混合
of 列表达式和 ORM 映射实体可以传递给
Insert.returning()
方法,然后传递该方法
以 ORM 结果从诸如
Select
,包括映射的实体将作为 ORM 映射对象在结果中传递。对 ORM
加载器选项(如 load_only() 和 selectinload()
)的支持有限
也存在。
将 RETURNING 记录与输入数据顺序相关联¶
将批量 INSERT 与 RETURNING 一起使用时,请务必注意,大多数数据库后端不对 RETURNING 记录的返回顺序提供正式保证,包括无法保证它们的顺序与 Importing 记录的顺序相对应。对于需要确保 RETURNING 记录可以与输入数据关联的应用程序,附加参数 Insert.returning.sort_by_parameter_order
,根据后端,可以使用特殊的 INSERT 格式
维护一个令牌,用于对返回的行进行适当的重新排序,
或者在某些情况下,例如在下面的示例中使用 SQLite 后端,
该作将一次 INSERT 一行:
>>> data = [
... {"name": "pearl", "fullname": "Pearl Krabs"},
... {"name": "plankton", "fullname": "Plankton"},
... {"name": "gary", "fullname": "Gary"},
... ]
>>> user_ids = session.scalars(
... insert(User).returning(User.id, sort_by_parameter_order=True), data
... )
INSERT INTO user_account (name, fullname) VALUES (?, ?) RETURNING id
[... (insertmanyvalues) 1/3 (ordered; batch not supported)] ('pearl', 'Pearl Krabs')
INSERT INTO user_account (name, fullname) VALUES (?, ?) RETURNING id
[insertmanyvalues 2/3 (ordered; batch not supported)] ('plankton', 'Plankton')
INSERT INTO user_account (name, fullname) VALUES (?, ?) RETURNING id
[insertmanyvalues 3/3 (ordered; batch not supported)] ('gary', 'Gary')
>>> for user_id, input_record in zip(user_ids, data):
... input_record["id"] = user_id
>>> print(data)
[{'name': 'pearl', 'fullname': 'Pearl Krabs', 'id': 6},
{'name': 'plankton', 'fullname': 'Plankton', 'id': 7},
{'name': 'gary', 'fullname': 'Gary', 'id': 8}]
2.0.10 版本中的新功能: 添加 Insert.returning.sort_by_parameter_order
这是在 insertManyValues 体系结构中实现的。
另请参阅
将 RETURNING 行与参数集相关联 - 为保证输入数据和结果行之间的对应关系而不显著降低性能而采取的方法的背景
使用异构参数字典¶
ORM 批量插入功能支持 “异构” 的参数字典列表,这基本上意味着 “单个字典可以有不同的键”。当检测到这种情况时,ORM 会将参数字典分成与每组键相对应的组,并相应地批处理到单独的 INSERT 语句中:
>>> users = session.scalars(
... insert(User).returning(User),
... [
... {
... "name": "spongebob",
... "fullname": "Spongebob Squarepants",
... "species": "Sea Sponge",
... },
... {"name": "sandy", "fullname": "Sandy Cheeks", "species": "Squirrel"},
... {"name": "patrick", "species": "Starfish"},
... {
... "name": "squidward",
... "fullname": "Squidward Tentacles",
... "species": "Squid",
... },
... {"name": "ehkrabs", "fullname": "Eugene H. Krabs", "species": "Crab"},
... ],
... )
INSERT INTO user_account (name, fullname, species)
VALUES (?, ?, ?), (?, ?, ?) RETURNING id, name, fullname, species
[... (insertmanyvalues) 1/1 (unordered)] ('spongebob', 'Spongebob Squarepants', 'Sea Sponge',
'sandy', 'Sandy Cheeks', 'Squirrel')
INSERT INTO user_account (name, species)
VALUES (?, ?) RETURNING id, name, fullname, species
[...] ('patrick', 'Starfish')
INSERT INTO user_account (name, fullname, species)
VALUES (?, ?, ?), (?, ?, ?) RETURNING id, name, fullname, species
[... (insertmanyvalues) 1/1 (unordered)] ('squidward', 'Squidward Tentacles',
'Squid', 'ehkrabs', 'Eugene H. Krabs', 'Crab')
在上面的示例中,将 translated 为
三个 INSERT 语句,按特定键集分组
在每个字典中,同时仍然保持行顺序,即
("name", "fullname", "species")
, (“名称”, “物种”)
, ("name","fullname", "species")
.
在 ORM 批量 INSERT 语句中发送 NULL 值¶
批量 ORM 插入功能借鉴了一种行为,该行为也存在于传统的“批量”插入行为以及整个 ORM 工作单元中,即包含NULL值的行使用不引用这些列的语句进行INSERTed;这里的基本原理是,包含服务器端 INSERT 默认值的后端和架构(这些默认值可能对存在 NULL 值而不是不存在值敏感)将按预期生成服务器端值。此默认行为的效果是将批量插入的批次分解为行数较少的更多批次:
>>> session.execute(
... insert(User),
... [
... {
... "name": "name_a",
... "fullname": "Employee A",
... "species": "Squid",
... },
... {
... "name": "name_b",
... "fullname": "Employee B",
... "species": "Squirrel",
... },
... {
... "name": "name_c",
... "fullname": "Employee C",
... "species": None,
... },
... {
... "name": "name_d",
... "fullname": "Employee D",
... "species": "Bluefish",
... },
... ],
... )
INSERT INTO user_account (name, fullname, species) VALUES (?, ?, ?)
[...] [('name_a', 'Employee A', 'Squid'), ('name_b', 'Employee B', 'Squirrel')]
INSERT INTO user_account (name, fullname) VALUES (?, ?)
[...] ('name_c', 'Employee C')
INSERT INTO user_account (name, fullname, species) VALUES (?, ?, ?)
[...] ('name_d', 'Employee D', 'Bluefish')
...
在上面,四行的批量 INSERT 被分成三个单独的语句,第二个语句被重新格式化,不引用包含 None
值的单个参数字典的 NULL 列。 此默认值
当数据集中的许多行包含随机 NULL 时,行为可能是不需要的
值,因为它会导致 “executeMany”作被分解为更大的
小型作的数量;特别是当依赖
insertmany值来减少语句的总数,这可能会对性能产生更大的影响。
要禁用将参数中的 None
值处理为单独的批处理,请传递执行选项 render_nulls=True
;这将导致所有 parameter 字典被等效处理,假设每个字典中的键集相同:
>>> session.execute(
... insert(User).execution_options(render_nulls=True),
... [
... {
... "name": "name_a",
... "fullname": "Employee A",
... "species": "Squid",
... },
... {
... "name": "name_b",
... "fullname": "Employee B",
... "species": "Squirrel",
... },
... {
... "name": "name_c",
... "fullname": "Employee C",
... "species": None,
... },
... {
... "name": "name_d",
... "fullname": "Employee D",
... "species": "Bluefish",
... },
... ],
... )
INSERT INTO user_account (name, fullname, species) VALUES (?, ?, ?)
[...] [('name_a', 'Employee A', 'Squid'), ('name_b', 'Employee B', 'Squirrel'), ('name_c', 'Employee C', None), ('name_d', 'Employee D', 'Bluefish')]
...
在上面,所有参数字典都在单个 INSERT 批处理中发送,包括第三个参数字典中存在的 None
值。
2.0.23 版本中的新功能: 添加了 render_nulls
执行选项,该选项
镜像旧版
Session.bulk_insert_mappings.render_nulls
参数。
用于连接表继承的批量 INSERT¶
ORM 批量插入建立在传统工作单元系统使用的内部系统之上,以便发出 INSERT 语句。这意味着,对于映射到多个表的 ORM 实体,通常是使用联接表继承映射的 ORM 实体,批量 INSERT作将为映射表示的每个表发出 INSERT 语句,从而将服务器生成的主键值正确地传输到依赖于它们的表行。这里也支持 RETURNING 功能,其中 ORM 将接收执行的每个 INSERT 语句的 Result
对象,然后将它们“水平拼接”在一起,以便返回的行包含插入的所有列的值:
>>> managers = session.scalars(
... insert(Manager).returning(Manager),
... [
... {"name": "sandy", "manager_name": "Sandy Cheeks"},
... {"name": "ehkrabs", "manager_name": "Eugene H. Krabs"},
... ],
... )
INSERT INTO employee (name, type) VALUES (?, ?) RETURNING id, name, type
[... (insertmanyvalues) 1/2 (ordered; batch not supported)] ('sandy', 'manager')
INSERT INTO employee (name, type) VALUES (?, ?) RETURNING id, name, type
[insertmanyvalues 2/2 (ordered; batch not supported)] ('ehkrabs', 'manager')
INSERT INTO manager (id, manager_name) VALUES (?, ?), (?, ?) RETURNING id, manager_name, id AS id__1
[... (insertmanyvalues) 1/1 (ordered)] (1, 'Sandy Cheeks', 2, 'Eugene H. Krabs')
提示
连接继承映射的批量 INSERT 要求 ORM 使用 Insert.returning.sort_by_parameter_order
parameter 的 API 中,以便它可以将
将 base 表中的行 RETURNING 到正在使用的参数集中
到 “sub” 表中插入 INSERT,这就是 SQLite 后端
上图透明地降级为使用非批处理语句。
此功能的背景位于
将 RETURNING 行与参数集相关联。
使用 SQL 表达式的 ORM 批量插入¶
ORM 批量插入功能支持添加一组固定的参数,其中可能包括要应用于每个目标行的 SQL 表达式。为了实现这一点,请将 Insert.values()
方法的使用(传递将应用于所有行的参数字典)与通常的批量调用形式结合使用,方法是在调用 Session.execute()
时包含包含单个行值的参数字典列表。
例如,给定一个包含 “timestamp” 列的 ORM 映射:
import datetime
class LogRecord(Base):
__tablename__ = "log_record"
id: Mapped[int] = mapped_column(primary_key=True)
message: Mapped[str]
code: Mapped[str]
timestamp: Mapped[datetime.datetime]
如果我们想要 INSERT 一系列 LogRecord
元素,每个元素都有一个唯一的
message
字段,但是我们想应用 SQL 函数 now()
到所有行,我们可以在 Insert.values()
中传递 timestamp
然后使用 “bulk” 模式传递其他记录:
>>> from sqlalchemy import func
>>> log_record_result = session.scalars(
... insert(LogRecord).values(code="SQLA", timestamp=func.now()).returning(LogRecord),
... [
... {"message": "log message #1"},
... {"message": "log message #2"},
... {"message": "log message #3"},
... {"message": "log message #4"},
... ],
... )
INSERT INTO log_record (message, code, timestamp)
VALUES (?, ?, CURRENT_TIMESTAMP), (?, ?, CURRENT_TIMESTAMP),
(?, ?, CURRENT_TIMESTAMP), (?, ?, CURRENT_TIMESTAMP)
RETURNING id, message, code, timestamp
[... (insertmanyvalues) 1/1 (unordered)] ('log message #1', 'SQLA', 'log message #2',
'SQLA', 'log message #3', 'SQLA', 'log message #4', 'SQLA')
>>> print(log_record_result.all())
[LogRecord('log message #1', 'SQLA', datetime.datetime(...)),
LogRecord('log message #2', 'SQLA', datetime.datetime(...)),
LogRecord('log message #3', 'SQLA', datetime.datetime(...)),
LogRecord('log message #4', 'SQLA', datetime.datetime(...))]
带有每行 SQL 表达式的 ORM 批量插入¶
Insert.values()
方法本身直接包含参数字典列表。在此中使用 Insert
构造时
方式,而不将任何参数字典列表传递给
Session.execute.params
参数,则批量 ORM 插入模式不是
used,而是 INSERT 语句完全按照给定和调用的方式呈现
恰好一次。此作模式对于通过
SQL 表达式,并且在使用 “upsert” 时也使用
语句,记录在本章后面的
ORM “upsert” 语句。
下面是一个人为的 INSERT 示例,它嵌入了每行 SQL 表达式,并以这种形式演示了 Insert.returning()
:
>>> from sqlalchemy import select
>>> address_result = session.scalars(
... insert(Address)
... .values(
... [
... {
... "user_id": select(User.id).where(User.name == "sandy"),
... "email_address": "sandy@company.com",
... },
... {
... "user_id": select(User.id).where(User.name == "spongebob"),
... "email_address": "spongebob@company.com",
... },
... {
... "user_id": select(User.id).where(User.name == "patrick"),
... "email_address": "patrick@company.com",
... },
... ]
... )
... .returning(Address),
... )
INSERT INTO address (user_id, email_address) VALUES
((SELECT user_account.id
FROM user_account
WHERE user_account.name = ?), ?), ((SELECT user_account.id
FROM user_account
WHERE user_account.name = ?), ?), ((SELECT user_account.id
FROM user_account
WHERE user_account.name = ?), ?) RETURNING id, user_id, email_address
[...] ('sandy', 'sandy@company.com', 'spongebob', 'spongebob@company.com',
'patrick', 'patrick@company.com')
>>> print(address_result.all())
[Address(email_address='sandy@company.com'),
Address(email_address='spongebob@company.com'),
Address(email_address='patrick@company.com')]
由于上面未使用批量 ORM 插入模式,因此不存在以下功能:
联接表继承 或其他多表映射,因为这需要多个 INSERT 语句。
异构参数集 不受支持 - VALUES 集中的每个元素必须具有相同的 列。
核心级缩放优化,例如 insertmany值不可用;语句需要确保参数的总数不超过后备数据库施加的限制。
由于上述原因,通常不建议将多个参数集与 Insert.values()
与 ORM INSERT 语句一起使用,除非有明确的理由,即正在使用 “upsert” 或需要在每个参数集中嵌入每行 SQL 表达式。
另请参阅
遗留会话批量 INSERT 方法¶
Session
包括用于执行 “bulk” INSERT 和 UPDATE 语句的遗留方法。这些方法与这些功能的 SQLAlchemy 2.0 版本共享实现,如 ORM Bulk INSERT Statements 和 ORM Bulk UPDATE by Primary Key 中所述,但缺少许多功能,即 RETURNING 支持以及会话同步支持。
例如,使用 Session.bulk_insert_mappings()
的代码可以按如下方式移植代码,从这个 mappings 示例开始:
session.bulk_insert_mappings(User, [{"name": "u1"}, {"name": "u2"}, {"name": "u3"}])
以上使用新 API 表示为:
from sqlalchemy import insert
session.execute(insert(User), [{"name": "u1"}, {"name": "u2"}, {"name": "u3"}])
另请参阅
ORM “upsert” 语句¶
使用 SQLAlchemy 的选定后端可能包括特定于方言的 Insert
结构,这些结构还能够执行 “upserts” 或 INSERT
其中,参数集中的现有行将转换为
一个 UPDATE 语句。通过 “existing row” ,这可能意味着行
它们共享相同的主键值,或者可能引用其他索引
行中被视为唯一的列;这是依赖的
关于正在使用的后端的能力。
SQLAlchemy 中包含的 dialects 包含特定于方言的 “upsert” API 功能:
SQLite - 使用INSERT
中记录的 Insert...冲突时 (Upsert)
PostgreSQL - 使用 INSERT... 中记录的Insert
冲突时 (Upsert)
MySQL/MariaDB - 使用INSERT
中记录的 Insert...ON DUPLICATE KEY UPDATE (Upsert) (重复密钥更新)
用户应查看上述部分,了解有关正确构建这些对象的背景信息;特别是,“UPSERT” 方法通常需要引用原始语句,因此该语句通常分两个单独的步骤构建。
第三方后端(如 External Dialects 中提到的那些)也可能具有类似的结构。
虽然 SQLAlchemy 还没有与后端无关的 upsert 结构,但上述
尽管如此,Insert
变体还是 ORM 兼容的,因为它们的使用方式与 Insert
构建体本身相同,如
带有每行 SQL 表达式的 ORM 批量插入,即通过将所需的行嵌入到 Insert.values()
方法中的 INSERT。在下面的示例中,SQLite insert()
函数用于生成包含“ON CONFLICT DO UPDATE”支持的 Insert
结构。然后将该语句传递给 Session.execute()
,在那里它正常进行,其另一个特征是传递给 Insert.values()
的参数字典被解释为 ORM 映射的属性键,而不是列名:
>>> from sqlalchemy.dialects.sqlite import insert as sqlite_upsert
>>> stmt = sqlite_upsert(User).values(
... [
... {"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"},
... ]
... )
>>> stmt = stmt.on_conflict_do_update(
... index_elements=[User.name], set_=dict(fullname=stmt.excluded.fullname)
... )
>>> session.execute(stmt)
INSERT INTO user_account (name, fullname)
VALUES (?, ?), (?, ?), (?, ?), (?, ?), (?, ?)
ON CONFLICT (name) DO UPDATE SET fullname = excluded.fullname
[...] ('spongebob', 'Spongebob Squarepants', 'sandy', 'Sandy Cheeks',
'patrick', 'Patrick Star', 'squidward', 'Squidward Tentacles',
'ehkrabs', 'Eugene H. Krabs')
<...>
将 RETURN 与 upsert 语句一起使用¶
从 SQLAlchemy ORM 的角度来看,upsert 语句看起来就像常规的
Insert
结构,其中包括 Insert.returning()
以与 UPSERT 语句相同的方式工作
带有每行 SQL 表达式的 ORM 批量插入,以便可以传递任何列表达式或相关的 ORM 实体类。继续上一节中的示例:
>>> result = session.scalars(
... stmt.returning(User), execution_options={"populate_existing": True}
... )
INSERT INTO user_account (name, fullname)
VALUES (?, ?), (?, ?), (?, ?), (?, ?), (?, ?)
ON CONFLICT (name) DO UPDATE SET fullname = excluded.fullname
RETURNING id, name, fullname, species
[...] ('spongebob', 'Spongebob Squarepants', 'sandy', 'Sandy Cheeks',
'patrick', 'Patrick Star', 'squidward', 'Squidward Tentacles',
'ehkrabs', 'Eugene H. Krabs')
>>> print(result.all())
[User(name='spongebob', fullname='Spongebob Squarepants'),
User(name='sandy', fullname='Sandy Cheeks'),
User(name='patrick', fullname='Patrick Star'),
User(name='squidward', fullname='Squidward Tentacles'),
User(name='ehkrabs', fullname='Eugene H. Krabs')]
上面的示例使用 RETURNING 为插入的每一行返回 ORM 对象,或者
被该语句更新。该示例还添加了对
填充 Existing execution 选项。此选项指示 Session
中已存在的行的 User
对象应为
使用新行中的数据刷新。对于纯嵌件
语句,此选项并不重要,因为生成的每一行都是
全新的主键标识。但是,当 Insert
还包含 “upsert” 选项时,它也可能会从已经存在的行中产生结果,因此可能已经在 Session
对象的标识映射中表示了主键标识。
另请参阅
按主键进行 ORM 批量更新¶
Update
构造可以与
Session.execute()
与 Insert
类似的
statement 的使用方式如 ORM Bulk INSERT Statements中所述,传递许多参数字典的列表,每个字典代表对应于单个主键值的单个行。这种用法不应与使用 Update
语句的更常见方式混淆。
ORM,使用显式 WHERE 子句,该子句记录在
具有自定义 WHERE 条件的 ORM UPDATE 和 DELETE。
对于 UPDATE 的 “bulk” 版本,根据 ORM 类制作一个 update()
结构,并将其传递给 Session.execute()
方法;生成的 Update
对象不应有值,并且通常
没有 WHERE 条件,即不使用 Update.values()
方法,并且通常不使用 Update.where(),
但可以在添加其他筛选条件的不寻常情况下使用。
传递 Update
构造以及参数字典列表(每个字典都包含一个完整的主键值)将调用 bulk
UPDATE 按主键模式,生成适当的 WHERE 条件以按主键匹配每一行,并使用 executemany
要针对 UPDATE 语句运行每个参数集,请执行以下作:
>>> from sqlalchemy import update
>>> session.execute(
... update(User),
... [
... {"id": 1, "fullname": "Spongebob Squarepants"},
... {"id": 3, "fullname": "Patrick Star"},
... {"id": 5, "fullname": "Eugene H. Krabs"},
... ],
... )
UPDATE user_account SET fullname=? WHERE user_account.id = ?
[...] [('Spongebob Squarepants', 1), ('Patrick Star', 3), ('Eugene H. Krabs', 5)]
<...>
请注意,每个参数字典都必须包含
每条记录,否则会引发错误。
与批量 INSERT 功能一样,此处也支持异构参数列表,其中参数将分组为 UPDATE 运行的子批次。
在 2.0.11 版本发生变更: 其他 WHERE 标准可以与
使用 Update.where()
按主键进行 ORM 批量更新
方法添加其他条件。 但是,此条件始终在
添加到已经存在的 WHERE 标准中,其中包括
primary key 值。
使用“按主键批量更新”功能时,RETURNING 功能不可用;多参数字典列表必须使用 DBAPI executemany,而 DBAPI 的通常形式通常不支持结果行。
在 2.0 版更改: 将 Update
构造传递给
Session.execute()
方法以及参数字典列表现在调用“批量更新”,它使用与旧版 Session.bulk_update_mappings()
方法。 与 1.x 系列相比,这是一个行为变化,其中
只有显式 WHERE 条件和内联 VALUES 才支持 Update
。
为具有多个参数集的 UPDATE 语句禁用按主键批量 ORM 更新¶
在以下情况下,会自动使用 ORM Bulk Update by Primary Key 功能,该功能为每条记录运行一个 UPDATE 语句,其中包括每个主键值的 WHERE 标准:
给出的 UPDATE 语句是针对 ORM 实体的Session
用于执行语句,而不是 CoreConnection
传递的参数是字典列表。
为了在不使用“ORM Bulk Update by Primary Key”的情况下调用 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')]
<...>
通过主键批量 UPDATE 以进行连接表继承¶
使用映射时,ORM 批量更新的行为与 ORM 批量插入类似
with join table inheritance;如
批量 INSERT 对于联接表继承,批量 UPDATE作将为映射中表示的每个表发出 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)]
<...>
遗留会话批量 UPDATE 方法¶
如传统会话批量 INSERT 方法中所述,
Session.bulk_update_mappings()
method
是 bulk update 的遗留形式,ORM 在解释给定主键参数的 update()
语句时在内部使用它;但是,使用旧版本时,不包括支持会话同步等功能。
示例如下:
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"},
],
)
另请参阅
带有自定义 WHERE 条件的 ORM UPDATE 和 DELETE¶
Update
和 Delete
构造,当使用自定义 WHERE 条件(即使用 Update.where()
和
Delete.where()
方法),可以通过将它们传递给 Session.execute()
在 ORM 上下文中调用,而无需使用 Session.execute.params
参数。对于 Update
,应使用 Update.values()
传递要更新的值。
这种使用模式不同于之前在 ORM Bulk UPDATE by Primary Key 中描述的功能
因为 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 工作单元
automation 支持能够发出单个 UPDATE 或 DELETE 语句
一次匹配多行,而不会产生复杂性。
这些作不提供 Python 内关系级联 - 假设为任何需要它的外键引用配置了 ON UPDATE CASCADE 和/或 ON DELETE CASCADE,否则如果强制执行外键引用,数据库可能会发出完整性冲突。有关一些示例,请参阅对 ORM 关系使用外键 ON DELETE 级联中的注释。
在 UPDATE 或 DELETE 之后,Session
中受相关表的 ON UPDATE CASCADE 或 ON DELETE CASCADE 影响的依赖对象,特别是引用现已删除的行的对象,可能仍引用这些对象。此问题在Session
后得到解决 过期,这通常发生在Session.commit()
上,也可以使用Session.expire_all()
强制执行。
启用 ORM 的 UPDATE 和 DELETE 不会自动处理联接表继承。请参阅部分 UPDATE/DELETE with Custom WHERE Criteria for Joined Table Inheritance 有关如何使用联接继承映射的说明。
包括将多态标识限制为单表继承映射的特定子类所需的 WHERE 标准 自动 .这仅适用于没有自己的 table 的子类映射器。
ORM 更新和删除作支持with_loader_criteria()
选项;这里的标准将被添加到正在发出的 UPDATE 或 DELETE 语句的 criteria 中,并在 “synchronize” 过程中被考虑在内。
要使用事件处理程序拦截启用 ORM 的 UPDATE 和 DELETE作,请使用该SessionEvents.do_orm_execute()
事件。
选择同步策略¶
当使用 update()
或 delete()
与使用 Session.execute()
的启用 ORM 的执行结合使用时,存在额外的 ORM 特定功能,它将使语句更改的状态与 Session
的身份映射中当前存在的对象的状态同步.“synchronize” 是指 UPDATEd 属性将使用新值刷新,或者至少过期,以便它们在下次访问时重新填充新值,并且 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')
<...>
execution 选项也可以使用
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'
- 这是默认值。'fetch'
策略将用于支持 RETURNING 的后端,其中包括除 MySQL 之外的所有 SQLAlchemy 原生驱动程序。如果不支持RETURNING,则 'evaluate'
策略。'fetch'
- 通过以下任一方式检索受影响行的主键标识 在 UPDATE 或 DELETE 之前执行 SELECT,或者使用 RETURNING(如果 Database 支持它,因此受 作可以使用新值(更新)刷新或从会话
(删除)。即使给定的update()
或delete()
也可以使用此同步策略 construct 使用UpdateBase.returning() 返回。
在 2.0 版更改: 当使用启用 ORM 的 UPDATE 和 DELETE 与 WHERE 标准时,显式UpdateBase.returning()
可以与'fetch'
同步策略结合使用。实际语句将包含'fetch'
策略要求和那些被要求。'evaluate'
- 这表示评估 Python 中 UPDATE 或 DELETE 语句中给出的 WHERE 条件,以在Session
中查找匹配的对象。这种方法不会向作添加任何 SQL 往返,并且在没有 RETURNING 支持的情况下,可能会更有效。对于具有复杂条件的 UPDATE 或 DELETE 语句,'evaluate'
策略可能无法评估 expression 的 Python 表达式,并将引发错误。如果发生这种情况,请使用'fetch'
策略。
提示
如果 SQL 表达式使用自定义运算符,则使用Operators.op()
或custom_op
功能,则Operators.op.python_impl
参数可用于指示“evaluate”
同步策略将使用的 Python 函数。
2.0 版的新Function。
警告
如果要在具有许多已过期对象的Session
上运行 UPDATE作,则应避免使用“evaluate”
策略,因为它必须刷新对象才能根据给定的 WHERE 标准测试它们,这将为每个对象发出 SELECT。在这种情况下,特别是如果后端支持 RETURNING,则应首选“fetch”
策略。False
- 不同步会话。此选项对于不支持 RETURNING 的后端可能很有用,其中“evaluate”
策略 无法使用。 在这种情况下,Session
保持不变,并且不会自动对应于发出的 UPDATE 或 DELETE 语句,如果存在通常对应于匹配的行的此类对象。
将 RETURN 与 UPDATE/DELETE 和自定义 WHERE 条件一起使用¶
UpdateBase.returning()
方法与启用 ORM 的 UPDATE 和 DELETE with 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 的支持还与同样使用 RETURNING 的 fetch
同步策略兼容。ORM 将适当地组织 RETURNING 中的列,以便同步继续进行,并且返回的 Result
将按请求的顺序包含请求的实体和 SQL 列。
2.0 版本的新Function: UpdateBase.returning()
可用于启用 ORM 的 UPDATE 和 DELETE,同时仍然保持与 fetch
同步策略的完全兼容性。
UPDATE/DELETE 使用自定义 WHERE 标准进行连接表继承¶
与
ORM Bulk UPDATE by Primary Key,每次调用 Session.execute()
时只发出一条 UPDATE 或 DELETE 语句。这意味着,当针对多表映射(例如联接表继承映射中的子类)运行 update()
或 delete()
语句时,该语句必须符合后端的当前功能,其中可能包括后端不支持引用多个表的 UPDATE 或 DELETE 语句,或者可能对此只有有限的支持。这意味着对于诸如 joined 继承子类之类的映射,带有 WHERE 条件功能的 UPDATE/DELETE 功能的 ORM 版本只能在有限范围内使用或根本不使用,具体取决于具体情况。
为连接表子类发出多行 UPDATE 语句的最直接方法是单独引用该子表。这意味着 Update()
构造应该只引用子类 table 的本地属性,如下例所示:
>>> 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 标准,但是必须以某种方式明确说明两个 table 之间的标准:
>>> 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,预计基表和子表中的行将同时被 DELETEd。要在不使用级联外键的情况下 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 的 UPDATE/DELETE with WHERE 功能最初是现在遗留的 Query
对象的一部分,位于 Query.update()
中
和 Query.delete()
方法。 这些方法仍然可用
并提供与
具有自定义 WHERE 条件的 ORM UPDATE 和 DELETE。主要区别在于 legacy 方法不提供显式 RETURNING 支持。