SqlAlchemy 2.0 中文文档(七十四)(2)https://developer.aliyun.com/article/1562362
新功能和改进 - 核心
布尔数据类型现在强制使用严格的 True/False/None 值
在版本 1.1 中,描述的更改将非本地布尔整数值强制为零/一/无的所有情况产生了一个意外的副作用,改变了当Boolean
遇到非整数值(如字符串)时的行为。特别是,先前会生成值False
的字符串值"0"
现在会生成True
。更糟糕的是,行为的更改只针对某些后端而不是其他后端,这意味着将字符串"0"
值发送给Boolean
的代码在不同后端上会不一致地中断。
这个问题的最终解决方案是不支持将字符串值与布尔值一起使用,因此在 1.2 中,如果传递了非整数/True/False/None 值,将引发严格的TypeError
。此外,只接受整数值 0 和 1。
为了适应希望对布尔值有更自由解释的应用程序,应使用TypeDecorator
。下面说明了一个配方,将允许对 1.1 之前的Boolean
数据类型进行“自由”行为:
from sqlalchemy import Boolean from sqlalchemy import TypeDecorator class LiberalBoolean(TypeDecorator): impl = Boolean def process_bind_param(self, value, dialect): if value is not None: value = bool(int(value)) return value
#4102 ### 悲观的断开连接检测添加到连接池
连接池文档长期以来一直提供了一个使用ConnectionEvents.engine_connect()
引擎事件在检出的连接上发出简单语句以测试其活动性的示例。现在,当与适当的方言一起使用时,此示例的功能已添加到连接池本身中。使用新参数create_engine.pool_pre_ping
,每个检出的连接在返回之前将被测试是否新鲜:
engine = create_engine("mysql+pymysql://", pool_pre_ping=True)
尽管“预检测”方法会在连接池检出时增加少量延迟,但对于典型的事务性应用(包括大多数 ORM 应用),这种开销很小,并且消除了获取到过期连接而引发错误、需要应用程序放弃或重试操作的问题。
该特性不适用于在进行中的事务或 SQL 操作中断开的连接。如果应用程序必须从中恢复,它需要使用自己的操作重试逻辑来预期这些错误。
另请参阅
断开连接处理 - 悲观
#3919 ### IN / NOT IN 运算符的空集合行为现在可配置;默认表达式简化
诸如column.in_([])
这样的表达式,默认情况下现在会产生表达式1 != 1
,而不是column != column
。这将改变查询的结果,该查询比较了一个在与空集合进行比较时求值为 NULL 的 SQL 表达式或列,产生了布尔值 false 或 true(对于 NOT IN),而不是 NULL。在这种情况下发出的警告也被移除了。可以使用create_engine()
的create_engine.empty_in_strategy
参数来获得旧行为。
在 SQL 中,IN 和 NOT IN 运算符不支持显式空值集合的比较;也就是说,这种语法是非法的:
mycolumn IN ()
为了解决这个问题,SQLAlchemy 和其他数据库库检测到这种情况,并渲染一个替代表达式,该表达式求值为 false,或者在 NOT IN 的情况下,求值为 true,根据“col IN ()”始终为 false 的理论,因为“空集合中没有任何东西”。通常为了生成一个跨数据库可移植且在 WHERE 子句的上下文中起作用的 false/true 常量,会使用一个简单的重言式,比如1 != 1
会求值为 false,1 = 1
会求值为 true(简单的常量“0”或“1”通常不能作为 WHERE 子句的目标)。
SQLAlchemy 在早期也采用了这种方法,但很快有人推测,如果“column”为 NULL,SQL 表达式column IN ()
不会求值为 false;相反,该表达式将产生 NULL,因为“NULL”表示“未知”,而在 SQL 中与 NULL 的比较通常产生 NULL。
为了模拟这个结果,SQLAlchemy 从使用1 != 1
改为使用表达式expr != expr
来处理空的“IN”,并使用expr = expr
处理空的“NOT IN”;也就是说,我们使用表达式的实际左侧而不是固定值。如果传递的表达式左侧评估为 NULL,则整体比较也会得到 NULL 结果,而不是 false 或 true。
不幸的是,用户最终抱怨这个表达式对一些查询规划器有非常严重的性能影响。在那时,当遇到空的 IN 表达式时添加了一个警告,支持 SQLAlchemy 继续保持“正确”,并敦促用户避免一般情况下生成空的 IN 谓词的代码,因为通常它们可以安全地省略。然而,在从输入变量动态构建查询的情况下,这在查询中是繁琐的,因为传入的值集可能为空。
最近几个月,对这个决定的最初假设进行了质疑。表达式“NULL IN ()”应该返回 NULL 的想法只是理论上的,无法测试,因为数据库不支持该语法。然而,事实证明,实际上可以通过模拟空集来询问关系数据库对“NULL IN ()”会返回什么值:
SELECT NULL IN (SELECT 1 WHERE 1 != 1)
通过上述测试,我们发现数据库本身无法就答案达成一致。被大多数人认为是最“正确”的数据库 PostgreSQL 返回 False;因为即使“NULL”代表“未知”,“空集”意味着没有任何内容,包括所有未知值。另一方面,MySQL 和 MariaDB 对上述表达式返回 NULL,采用更常见的“所有与 NULL 的比较都返回 NULL”的行为。
SQLAlchemy 的 SQL 架构比最初做出这个设计决定时更复杂,因此我们现在可以在 SQL 字符串编译时调用任一行为。以前,转换为比较表达式是在构造时完成的,也就是说,在调用ColumnOperators.in_()
或ColumnOperators.notin_()
操作符时。通过编译时行为,方言本身可以被指示调用任一方法,即“静态”的1 != 1
比较或“动态”的expr != expr
比较。默认已经更改为“静态”比较,因为这与 PostgreSQL 的行为一致,而且这也是绝大多数用户喜欢的。这将改变查询结果,特别是对比较空表达式和空集的查询,特别是查询否定where(~null_expr.in_([]))
,因为现在这将评估为 true 而不是 NULL。
现在可以使用标志 create_engine.empty_in_strategy
控制此行为,默认为"static"
设置,但也可以设置为"dynamic"
或"dynamic_warn"
,其中"dynamic_warn"
设置等效于以前的行为,即发出 expr != expr
以及性能警告。但预计大多数用户将欣赏"static"
默认设置。
#3907 ### 支持延迟扩展的 IN 参数集,允许具有缓存语句的 IN 表达式
添加了一种名为“expanding”的新类型的 bindparam()
。这是用于 IN
表达式的,其中元素列表在语句执行时被渲染为单独的绑定参数,而不是在语句编译时。这允许将单个绑定参数名称链接到多个元素的 IN 表达式,并且允许使用查询缓存与 IN 表达式。新功能允许相关功能的“select in”加载和“polymorphic in”加载利用烘焙查询扩展以减少调用开销:
stmt = select([table]).where(table.c.col.in_(bindparam("foo", expanding=True))) conn.execute(stmt, {"foo": [1, 2, 3]})
在 1.2 系列中,该功能应被视为 实验性的。
#3953 ### 对比运算符的展开操作优先级
对于诸如 IN、LIKE、等于、IS、MATCH 等比较运算符的运算符优先级已被展开为一个级别。当组合比较运算符时,将生成更多的括号化效果,例如:
(column("q") == null()) != (column("y") == null())
现在将生成 (q IS NULL) != (y IS NULL)
而不是 q IS NULL != y IS NULL
。
#3999 ### 支持对表、列的 SQL 注释,包括 DDL、反射
数据核心现支持与表和列相关联的字符串注释。这些通过 Table.comment
和 Column.comment
参数指定:
Table( "my_table", metadata, Column("q", Integer, comment="the Q value"), comment="my Q table", )
上述 DDL 将在创建表时适当渲染,以将上述注释与模式中的表/列关联起来。当上述表以自动加载或通过 Inspector.get_columns()
进行检查时,将包括这些注释。表注释也可以使用 Inspector.get_table_comment()
方法独立获取。
当前后端支持包括 MySQL、PostgreSQL 和 Oracle。
#1546 ### 支持 DELETE 的多表条件
Delete
构造现在支持多表条件,已在支持的后端实现,目前这些后端包括 PostgreSQL、MySQL 和 Microsoft SQL Server(对当前不工作的 Sybase 方言也添加了支持)。该功能的工作方式与 0.7 和 0.8 系列中首次引入的 UPDATE 的多表条件相同。
给出一个语句如下:
stmt = ( users.delete() .where(users.c.id == addresses.c.id) .where(addresses.c.email_address.startswith("ed%")) ) conn.execute(stmt)
在 PostgreSQL 后端上,上述语句生成的 SQL 将呈现为:
DELETE FROM users USING addresses WHERE users.id = addresses.id AND (addresses.email_address LIKE %(email_address_1)s || '%%')
另请参阅
多表删除
#959 ### 新的 “autoescape” 选项用于 startswith()、endswith()
“autoescape” 参数已添加到 ColumnOperators.startswith()
、ColumnOperators.endswith()
、ColumnOperators.contains()
。当将此参数设置为 True
时,将自动使用转义字符转义所有 %
、_
的出现,默认为斜杠 /
;转义字符本身的出现也会被转义。斜杠用于避免与诸如 PostgreSQL 的 standard_confirming_strings
、MySQL 的 NO_BACKSLASH_ESCAPES
等设置发生冲突。现在可以使用现有的 “escape” 参数来更改自动转义字符,如果需要的话。
注意
从 1.2.0 的初始实现 1.2.0b2 开始,此功能已更改,现在 autoescape 被传递为布尔值,而不是用作转义字符的特定字符。
诸如以下表达式:
>>> column("x").startswith("total%score", autoescape=True)
呈现为:
x LIKE :x_1 || '%' ESCAPE '/'
其中参数 “x_1” 的值为 'total/%score'
。
同样,具有反斜杠的表达式:
>>> column("x").startswith("total/score", autoescape=True)
将以相同方式呈现,参数 “x_1” 的值为 'total//score'
。
#2694 ### “float” 数据类型增加更强的类型化
一系列更改允许使用 Float
数据类型更强烈地将其与 Python 浮点值联系起来,而不是更通用的 Numeric
。这些更改主要涉及确保 Python 浮点值不会错误地被强制转换为 Decimal()
,并且在需要时被强制转��为 float
,在结果方面,如果应用程序正在处理普通浮点数。
- 当传递给 SQL 表达式的普通 Python “float” 值现在将被拉入具有类型
Float
的字面参数中;之前,该类型为Numeric
,带有默认的“asdecimal=True”标志,这意味着结果类型将被强制转换为Decimal()
。特别是,这将在 SQLite 上发出令人困惑的警告:
float_value = connection.scalar( select([literal(4.56)]) # the "BindParameter" will now be # Float, not Numeric(asdecimal=True) )
Numeric
、Float
和Integer
之间的数学运算现在将保留结果表达式的类型Numeric
或Float
,包括asdecimal
标志以及类型是否应为Float
:
# asdecimal flag is maintained expr = column("a", Integer) * column("b", Numeric(asdecimal=False)) assert expr.type.asdecimal == False # Float subclass of Numeric is maintained expr = column("a", Integer) * column("b", Float()) assert isinstance(expr.type, Float)
- 如果 DBAPI 已知支持本地
Decimal()
模式,则Float
数据类型将无条件地将float()
处理器应用于结果值。某些后端不总是保证浮点数返回为普通浮点数,而不是诸如 MySQL 等精度数字。
对 GROUPING SETS、CUBE、ROLLUP 的支持
GROUPING SETS、CUBE、ROLLUP 三者都可以通过 func
命名空间使用。对于 CUBE 和 ROLLUP,在之前的版本中这些函数已经可以使用,但对于 GROUPING SETS,在编译器中添加了一个占位符以允许空间。这三个函数现在在文档中命名:
>>> from sqlalchemy import select, table, column, func, tuple_ >>> t = table("t", column("value"), column("x"), column("y"), column("z"), column("q")) >>> stmt = select([func.sum(t.c.value)]).group_by( ... func.grouping_sets( ... tuple_(t.c.x, t.c.y), ... tuple_(t.c.z, t.c.q), ... ) ... ) >>> print(stmt) SELECT sum(t.value) AS sum_1 FROM t GROUP BY GROUPING SETS((t.x, t.y), (t.z, t.q))
用于具有上下文默认生成器的多值插入的参数助手
默认生成函数,例如在上下文敏感的默认函数中描述的函数,可以通过DefaultExecutionContext.current_parameters
属性查看与语句相关的当前参数。然而,在通过Insert.values()
方法指定多个 VALUES 子句的Insert
构造中,用户定义的函数会被多次调用,每个参数集一次,但是无法知道DefaultExecutionContext.current_parameters
中的哪个键子集适用于该列。添加了一个新函数DefaultExecutionContext.get_current_parameters()
,其中包括一个关键字参数DefaultExecutionContext.get_current_parameters.isolate_multiinsert_groups
默认为True
,执行额外的工作,提供一个DefaultExecutionContext.current_parameters
的子字典,其中的名称局限于当前正在处理的 VALUES 子句:
def mydefault(context): return context.get_current_parameters()["counter"] + 12 mytable = Table( "mytable", metadata_obj, Column("counter", Integer), Column("counter_plus_twelve", Integer, default=mydefault, onupdate=mydefault), ) stmt = mytable.insert().values([{"counter": 5}, {"counter": 18}, {"counter": 20}]) conn.execute(stmt)
#4075 ### 布尔数据类型现在强制使用严格的 True/False/None 值
在版本 1.1 中,将非本地布尔整数值强制转换为零/一/None 的所有情况中描述的更改产生了一个意外的副作用,改变了当Boolean
遇到非整数值(如字符串)时的行为。特别是,先前会生成值False
的字符串值"0"
,现在会生成True
。更糟糕的是,行为的变化只针对某些后端而不是其他后端,这意味着将字符串"0"
值发送给Boolean
的代码在不同后端上会不一致地出现故障。
这个问题的最终解决方案是不支持将字符串值与布尔值一起使用,因此在 1.2 版本中,如果传递了非整数/True/False/None 值,将会引发严格的TypeError
。此外,只有整数值 0 和 1 会被接受。
为了适应希望对布尔值有更自由解释的应用程序,应该使用TypeDecorator
。下面演示了一个配方,可以允许在 1.1 版本之前的Boolean
数据类型的“自由”行为:
from sqlalchemy import Boolean from sqlalchemy import TypeDecorator class LiberalBoolean(TypeDecorator): impl = Boolean def process_bind_param(self, value, dialect): if value is not None: value = bool(int(value)) return value
将悲观的断开检测添加到连接池
连接池文档长期以来一直提供了一个使用ConnectionEvents.engine_connect()
引擎事件在检出的连接上发出简单语句以测试其活动性的配方。现在,当与适当的方言一起使用时,此配方的功能已经添加到连接池本身中。使用新参数create_engine.pool_pre_ping
,每个检出的连接在返回之前都会被测试是否仍然有效:
engine = create_engine("mysql+pymysql://", pool_pre_ping=True)
虽然“预检”方法会在连接池检出时增加一小部分延迟,但对于典型的面向事务的应用程序(其中包括大多数 ORM 应用程序),这种开销是很小的,并且消除了获取到一个过时连接会引发错误的问题,需要应用程序放弃或重试操作。
该功能不适用于在进行中的事务或 SQL 操作中断开的连接。如果应用程序必须从这些错误中恢复,它需要使用自己的操作重试逻辑来预期这些错误。
另请参阅
断开处理 - 悲观
IN / NOT IN 运算符的空集合行为现在是可配置的;默认表达式简化
例如,假设column.in_([])
这样的表达式被假定为 false,默认情况下现在会产生表达式1 != 1
,而不是column != column
。这将改变查询结果,如果比较 SQL 表达式或列与空集合时评估为 NULL,则会产生布尔值 false 或 true(对于 NOT IN),而不是 NULL。在这种情况下发出的警告也被移除了。可以使用create_engine.empty_in_strategy
参数来create_engine()
,以保留旧的行为。
在 SQL 中,IN 和 NOT IN 运算符不支持与明确为空的值集合进行比较;也就是说,以下语法是不合法的:
mycolumn IN ()
为了解决这个问题,SQLAlchemy 和其他数据库库检测到这种情况,并生成一个替代表达式,该表达式评估为 false,或者在 NOT IN 的情况下,根据“col IN ()”始终为 false 的理论,评估为 true,因为“空集合”中没有任何内容。通常,为了生成一个跨数据库可移植且在 WHERE 子句上下文中起作用的 false/true 常量,会使用一个简单的重言式,比如1 != 1
评估为 false,1 = 1
评估为 true(一个简单的常量“0”或“1”通常不能作为 WHERE 子句的目标)。
SQLAlchemy 在早期也采用了这种方法,但很快就有人推测,如果 SQL 表达式column IN ()
中的“column”为 NULL,则不会评估为 false;相反,该表达式会产生 NULL,因为“NULL”表示“未知”,而在 SQL 中与 NULL 的比较通常会产生 NULL。
为了模拟这个结果,SQLAlchemy 从使用1 != 1
改为使用表达式expr != expr
来表示空的“IN”,以及使用expr = expr
来表示空的“NOT IN”;也就是说,我们不再使用固定值,而是使用表达式的实际左侧。如果传递的表达式左侧评估为 NULL,则比较整体也会得到 NULL 结果,而不是 false 或 true。
不幸的是,用户最终抱怨说这个表达式对一些查询规划器有非常严重的性能影响。在那时,当遇到空的 IN 表达式时,添加了一个警告,建议 SQLAlchemy 继续保持“正确”,并敦促用户避免生成通常可以安全省略的空 IN 谓词的代码。然而,在动态构建查询的情况下,这在输入变量为空时可能会带来负担。
近几个月来,对这个决定的原始假设受到了质疑。认为表达式“NULL IN ()”应该返回 NULL 的想法只是理论上的,无法进行测试,因为数据库不支持该语法。然而,事实证明,你确实可以询问关系数据库关于“NULL IN ()”将返回什么值,方法是模拟空集如下:
SELECT NULL IN (SELECT 1 WHERE 1 != 1)
通过上述测试,我们发现数据库本身无法就答案达成一致。大多数人认为最“正确”的数据库 PostgreSQL 返回 False;因为即使“NULL”表示“未知”,“空集”表示什么都没有,包括所有未知的值。另一方面,MySQL 和 MariaDB 返回上述表达式的 NULL,默认为“所有与 NULL 的比较都返回 NULL”的更常见行为。
SQLAlchemy 的 SQL 架构比最初做出这个设计决定时更复杂,因此我们现在可以在 SQL 字符串编译时调用任一行为。先前,将转换为比较表达式是在构建时完成的,也就是说,在调用 ColumnOperators.in_()
或 ColumnOperators.notin_()
操作符时完成。通过编译时行为,方言本身可以被指示调用任一方法,即“static” 1 != 1
比较或“dynamic” expr != expr
比较。默认值已经更改为“static”比较,因为这与 PostgreSQL 在任何情况下的行为一致,这也是绝大多数用户喜欢的。这将改变查询空表达式与空集的比较结果,特别是查询否定 where(~null_expr.in_([]))
的查询,因为现在这将计算为 true 而不是 NULL。
可以使用标志create_engine.empty_in_strategy
来控制行为,该标志默认为"static"
设置,但也可以设置为"dynamic"
或"dynamic_warn"
,其中"dynamic_warn"
设置等效于以前的行为,即发出expr != expr
以及性能警告。但预计大多数用户会喜欢"static"
默认值。
晚扩展的 IN 参数集允许使用缓存语句的 IN 表达式
添加了一种名为“expanding”的新类型的bindparam()
。这用于在IN
表达式中,元素列表在语句执行时被渲染为单独的绑定参数,而不是在语句编译时。这允许将单个绑定参数名称链接到多个元素的 IN 表达式,并允许使用查询缓存与 IN 表达式一起使用。新功能允许“select in”加载和“polymorphic in”加载相关功能利用烘焙查询扩展以减少调用开销:
stmt = select([table]).where(table.c.col.in_(bindparam("foo", expanding=True))) conn.execute(stmt, {"foo": [1, 2, 3]})
该功能应被视为实验性,属于 1.2 系列。
比较运算符的操作符优先级已经被展开
对于 IN、LIKE、equals、IS、MATCH 和其他比较运算符等运算符的操作符优先级已被展开为一个级别。当比较运算符组合在一起时,将生成更多的括号,例如:
(column("q") == null()) != (column("y") == null())
现在将生成(q IS NULL) != (y IS NULL)
而不是q IS NULL != y IS NULL
。
支持在表、列上添加 SQL 注释,包括 DDL、反射
核心支持与表和列相关的字符串注释。这些通过Table.comment
和Column.comment
参数指定:
Table( "my_table", metadata, Column("q", Integer, comment="the Q value"), comment="my Q table", )
上述 DDL 将在表创建时适当地呈现,以将上述注释与模式中的表/列关联起来。当上述表被自动加载或使用Inspector.get_columns()
检查时,注释将被包含在内。表注释也可以通过Inspector.get_table_comment()
方法独立获取。
当前后端支持包括 MySQL、PostgreSQL 和 Oracle。
DELETE 的多表条件支持
Delete
构造现在支持多表条件,已在支持的后端实现,目前这些后端是 PostgreSQL、MySQL 和 Microsoft SQL Server(支持也已添加到当前不工作的 Sybase 方言)。该功能的工作方式与 0.7 和 0.8 系列中首次引入的 UPDATE 的多表条件相同。
给定语句如下:
stmt = ( users.delete() .where(users.c.id == addresses.c.id) .where(addresses.c.email_address.startswith("ed%")) ) conn.execute(stmt)
在 PostgreSQL 后端上,上述语句的结果 SQL 将呈现为:
DELETE FROM users USING addresses WHERE users.id = addresses.id AND (addresses.email_address LIKE %(email_address_1)s || '%%')
另请参见
多表删除
新的“autoescape”选项用于 startswith(),endswith()
“autoescape”参数被添加到ColumnOperators.startswith()
,ColumnOperators.endswith()
,ColumnOperators.contains()
中。当将此参数设置为True
时,将自动使用转义字符转义所有出现的%
、_
,默认为斜杠/
;转义字符本身的出现也会被转义。斜杠用于避免与诸如 PostgreSQL 的standard_confirming_strings
(从 PostgreSQL 9.1 开始更改默认值)和 MySQL 的NO_BACKSLASH_ESCAPES
设置等设置发生冲突。现在可以使用现有的“escape”参数来更改自动转义字符,如果需要的话。
注意
从 1.2.0 的初始实现 1.2.0b2 开始,此功能已更改,现在 autoescape 被传递为布尔值,而不是用作转义字符的特定字符。
例如:
>>> column("x").startswith("total%score", autoescape=True)
渲染为:
x LIKE :x_1 || '%' ESCAPE '/'
其中参数“x_1”的值为'total/%score'
。
同样,具有反斜杠的表达式:
>>> column("x").startswith("total/score", autoescape=True)
将以相同方式渲染,参数“x_1”的值为'total//score'
。
对“float”数据类型进行了更强的类型化
一系列更改允许使用Float
数据类型更强烈地将其与 Python 浮点值关联起来,而不是更通用的Numeric
。这些更改主要涉及确保 Python 浮点值不会错误地被强制转换为Decimal()
,并且在需要时,如果应用程序正在处理普通浮点数,则会被强制转换为float
。
- 传递给 SQL 表达式的普通 Python“float”值现在将被拉入具有类型
Float
的文字参数;以前,类型为Numeric
,带有默认的“asdecimal=True”标志,这意味着结果类型将强制转换为Decimal()
。特别是,这将在 SQLite 上发出令人困惑的警告:
float_value = connection.scalar( select([literal(4.56)]) # the "BindParameter" will now be # Float, not Numeric(asdecimal=True) )
- 在
Numeric
、Float
和Integer
之间的数学操作现在将保留结果表达式的类型,包括asdecimal
标志以及类型是否应为Float
:
# asdecimal flag is maintained expr = column("a", Integer) * column("b", Numeric(asdecimal=False)) assert expr.type.asdecimal == False # Float subclass of Numeric is maintained expr = column("a", Integer) * column("b", Float()) assert isinstance(expr.type, Float)
Float
数据类型将始终将float()
处理器应用于结果值,如果 DBAPI 已知支持原生Decimal()
模式。一些后端并不总是保证浮点数作为普通浮点数返回,而不是像 MySQL 这样的精度数字。
支持 GROUPING SETS、CUBE、ROLLUP
GROUPING SETS、CUBE、ROLLUP 这三个功能都可以通过 func
命名空间来调用。在 CUBE 和 ROLLUP 的情况下,这些函数在之前的版本中已经可以使用,但是对于 GROUPING SETS,编译器中添加了一个占位符以允许使用这个功能。现在文档中已经命名了这三个函数:
>>> from sqlalchemy import select, table, column, func, tuple_ >>> t = table("t", column("value"), column("x"), column("y"), column("z"), column("q")) >>> stmt = select([func.sum(t.c.value)]).group_by( ... func.grouping_sets( ... tuple_(t.c.x, t.c.y), ... tuple_(t.c.z, t.c.q), ... ) ... ) >>> print(stmt) SELECT sum(t.value) AS sum_1 FROM t GROUP BY GROUPING SETS((t.x, t.y), (t.z, t.q))
多值插入的参数辅助器,带有上下文默认生成器
默认生成函数,例如在上下文敏感的默认函数中描述的函数,可以通过DefaultExecutionContext.current_parameters
属性查看与语句相关的当前参数。然而,在通过Insert.values()
方法指定多个 VALUES 子句的Insert
构造中,用户定义的函数会被多次调用,每个参数集一次,但是无法知道DefaultExecutionContext.current_parameters
中的哪些键子集适用于该列。添加了一个新函数DefaultExecutionContext.get_current_parameters()
,其中包括一个关键字参数DefaultExecutionContext.get_current_parameters.isolate_multiinsert_groups
默认为True
,执行额外的工作,提供一个DefaultExecutionContext.current_parameters
的子字典,其中的名称局限于当前正在处理的 VALUES 子句:
def mydefault(context): return context.get_current_parameters()["counter"] + 12 mytable = Table( "mytable", metadata_obj, Column("counter", Integer), Column("counter_plus_twelve", Integer, default=mydefault, onupdate=mydefault), ) stmt = mytable.insert().values([{"counter": 5}, {"counter": 18}, {"counter": 20}]) conn.execute(stmt)
SqlAlchemy 2.0 中文文档(七十四)(4)https://developer.aliyun.com/article/1562364