SqlAlchemy 2.0 中文文档(二)(2)

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: SqlAlchemy 2.0 中文文档(二)

SqlAlchemy 2.0 中文文档(二)(1)https://developer.aliyun.com/article/1560323


LATERAL 相关性

LATERAL 相关性是 SQL 相关性的一种特殊子类,它允许可选单元在单个 FROM 子句内引用另一个可选单元。这是一个极其特殊的用例,虽然是 SQL 标准的一部分,但只有最近版本的 PostgreSQL 已知支持。

通常,如果 SELECT 语句在其 FROM 子句中引用了table1 JOIN (SELECT ...) AS subquery,则右侧的子查询可能不会引用左侧的“table1”表达式;相关联可能只引用完全包围此 SELECT 的另一个 SELECT 的表。LATERAL 关键字允许我们改变这种行为,允许来自右侧 JOIN 的相关联。

SQLAlchemy 支持使用Select.lateral()方法实现此功能,该方法创建一个称为Lateral的对象。LateralSubqueryAlias位于同一家族,但在将构造添加到封闭 SELECT 的 FROM 子句时还包括相关联行为。以下示例说明了使用 LATERAL 的 SQL 查询,选择“用户帐户/电子邮件地址计数”数据,如前一节所讨论的:

>>> subq = (
...     select(
...         func.count(address_table.c.id).label("address_count"),
...         address_table.c.email_address,
...         address_table.c.user_id,
...     )
...     .where(user_table.c.id == address_table.c.user_id)
...     .lateral()
... )
>>> stmt = (
...     select(user_table.c.name, subq.c.address_count, subq.c.email_address)
...     .join_from(user_table, subq)
...     .order_by(user_table.c.id, subq.c.email_address)
... )
>>> print(stmt)
SELECT  user_account.name,  anon_1.address_count,  anon_1.email_address
FROM  user_account
JOIN  LATERAL  (SELECT  count(address.id)  AS  address_count,
address.email_address  AS  email_address,  address.user_id  AS  user_id
FROM  address
WHERE  user_account.id  =  address.user_id)  AS  anon_1
ON  user_account.id  =  anon_1.user_id
ORDER  BY  user_account.id,  anon_1.email_address 

在上述示例中,JOIN 的右侧是一个子查询,它与左侧的user_account表相关联。

当使用Select.lateral()时,Select.correlate()Select.correlate_except() 方法的行为也会应用于Lateral 结构。

另请参阅

Lateral

Select.lateral() ## UNION、UNION ALL 和其他集合操作

在 SQL 中,SELECT 语句可以使用 UNION 或 UNION ALL SQL 操作合并在一起,它产生由一个或多个语句一起产生的所有行的集合。还可以进行其他集合操作,例如 INTERSECT [ALL] 和 EXCEPT [ALL]。

SQLAlchemy 的Select 结构支持使用像union()intersect()except_() 这样的函数进行此类组合,以及“all”对应项 union_all()intersect_all()except_all()。这些函数都接受任意数量的子可选择项,通常是Select 结构,但也可以是现有的组合。

这些函数生成的构造物是 CompoundSelect,其使用方式与 Select 构造物相同,只是方法较少。例如,由 union_all() 生成的 CompoundSelect 可以直接使用 Connection.execute() 调用:

>>> from sqlalchemy import union_all
>>> stmt1 = select(user_table).where(user_table.c.name == "sandy")
>>> stmt2 = select(user_table).where(user_table.c.name == "spongebob")
>>> u = union_all(stmt1, stmt2)
>>> with engine.connect() as conn:
...     result = conn.execute(u)
...     print(result.all())
BEGIN  (implicit)
SELECT  user_account.id,  user_account.name,  user_account.fullname
FROM  user_account
WHERE  user_account.name  =  ?
UNION  ALL  SELECT  user_account.id,  user_account.name,  user_account.fullname
FROM  user_account
WHERE  user_account.name  =  ?
[generated  in  ...]  ('sandy',  'spongebob')
[(2, 'sandy', 'Sandy Cheeks'), (1, 'spongebob', 'Spongebob Squarepants')]
ROLLBACK 

要将 CompoundSelect 用作子查询,就像 Select 一样,它提供了一个 SelectBase.subquery() 方法,该方法将生成一个带有 FromClause.c 集合的 Subquery 对象,该集合可以在封闭的 select() 中引用:

>>> u_subq = u.subquery()
>>> stmt = (
...     select(u_subq.c.name, address_table.c.email_address)
...     .join_from(address_table, u_subq)
...     .order_by(u_subq.c.name, address_table.c.email_address)
... )
>>> with engine.connect() as conn:
...     result = conn.execute(stmt)
...     print(result.all())
BEGIN  (implicit)
SELECT  anon_1.name,  address.email_address
FROM  address  JOIN
  (SELECT  user_account.id  AS  id,  user_account.name  AS  name,  user_account.fullname  AS  fullname
  FROM  user_account
  WHERE  user_account.name  =  ?
UNION  ALL
  SELECT  user_account.id  AS  id,  user_account.name  AS  name,  user_account.fullname  AS  fullname
  FROM  user_account
  WHERE  user_account.name  =  ?)
AS  anon_1  ON  anon_1.id  =  address.user_id
ORDER  BY  anon_1.name,  address.email_address
[generated  in  ...]  ('sandy',  'spongebob')
[('sandy', 'sandy@sqlalchemy.org'), ('sandy', 'sandy@squirrelpower.org'), ('spongebob', 'spongebob@sqlalchemy.org')]
ROLLBACK 

从并集中选择 ORM 实体

前面的例子演示了如何构造一个 UNION,给定两个 Table 对象,然后返回数据库行。如果我们想要使用 UNION 或其他集合操作来选择行,然后将其作为 ORM 对象接收,有两种方法可以使用。在这两种情况下,我们首先构造一个 select()CompoundSelect 对象,该对象表示我们想要执行的 SELECT / UNION / 等语句;这个语句应该针对目标 ORM 实体或它们的基础映射 Table 对象组成:

>>> stmt1 = select(User).where(User.name == "sandy")
>>> stmt2 = select(User).where(User.name == "spongebob")
>>> u = union_all(stmt1, stmt2)

对于一个简单的 SELECT 和 UNION,如果它还没有嵌套在子查询中,那么可以经常在 ORM 对象获取的上下文中使用Select.from_statement()方法。通过这种方法,UNION 语句表示整个查询;在使用Select.from_statement()之后,不能添加额外的条件:

>>> orm_stmt = select(User).from_statement(u)
>>> with Session(engine) as session:
...     for obj in session.execute(orm_stmt).scalars():
...         print(obj)
BEGIN  (implicit)
SELECT  user_account.id,  user_account.name,  user_account.fullname
FROM  user_account
WHERE  user_account.name  =  ?  UNION  ALL  SELECT  user_account.id,  user_account.name,  user_account.fullname
FROM  user_account
WHERE  user_account.name  =  ?
[generated  in  ...]  ('sandy',  'spongebob')
User(id=2, name='sandy', fullname='Sandy Cheeks')
User(id=1, name='spongebob', fullname='Spongebob Squarepants')
ROLLBACK 

要以更灵活的方式将 UNION 或其他集合相关的构造用作实体相关组件,可以使用CompoundSelect构造将其组织到一个子查询中,然后使用aliased()函数将其链接到  ORM 对象。这与在 ORM 实体子查询/CTEs  中引入的方式相同,首先创建我们想要的实体到子查询的临时“映射”,然后从新实体中选择,就像它是任何其他映射类一样。在下面的示例中,我们可以添加额外的条件,比如在  UNION 之外进行 ORDER BY,因为我们可以过滤或按子查询导出的列进行排序:

>>> user_alias = aliased(User, u.subquery())
>>> orm_stmt = select(user_alias).order_by(user_alias.id)
>>> with Session(engine) as session:
...     for obj in session.execute(orm_stmt).scalars():
...         print(obj)
BEGIN  (implicit)
SELECT  anon_1.id,  anon_1.name,  anon_1.fullname
FROM  (SELECT  user_account.id  AS  id,  user_account.name  AS  name,  user_account.fullname  AS  fullname
FROM  user_account
WHERE  user_account.name  =  ?  UNION  ALL  SELECT  user_account.id  AS  id,  user_account.name  AS  name,  user_account.fullname  AS  fullname
FROM  user_account
WHERE  user_account.name  =  ?)  AS  anon_1  ORDER  BY  anon_1.id
[generated  in  ...]  ('sandy',  'spongebob')
User(id=1, name='spongebob', fullname='Spongebob Squarepants')
User(id=2, name='sandy', fullname='Sandy Cheeks')
ROLLBACK 

另请参阅

从 UNIONs 和其他集合操作中选择实体 - 在 ORM 查询指南中的 ## EXISTS 子查询

SQL EXISTS 关键字是与标量子查询一起使用的运算符,根据 SELECT 语句是否返回行来返回布尔值 true 或 false。SQLAlchemy 包含一个称为ScalarSelect的对象变体,它将生成一个 EXISTS 子查询,并且最方便地使用SelectBase.exists()方法生成。下面我们生成一个 EXISTS,以便我们可以返回user_account中有多个相关行的行:

>>> subq = (
...     select(func.count(address_table.c.id))
...     .where(user_table.c.id == address_table.c.user_id)
...     .group_by(address_table.c.user_id)
...     .having(func.count(address_table.c.id) > 1)
... ).exists()
>>> with engine.connect() as conn:
...     result = conn.execute(select(user_table.c.name).where(subq))
...     print(result.all())
BEGIN  (implicit)
SELECT  user_account.name
FROM  user_account
WHERE  EXISTS  (SELECT  count(address.id)  AS  count_1
FROM  address
WHERE  user_account.id  =  address.user_id  GROUP  BY  address.user_id
HAVING  count(address.id)  >  ?)
[...]  (1,)
[('sandy',)]
ROLLBACK 

EXISTS 构造更常用于否定,例如 NOT EXISTS,因为它提供了一种 SQL 效率高的形式来定位一个相关表没有行的行。下面我们选择没有电子邮件地址的用户名称;注意第二个 WHERE 子句中使用的二进制否定运算符 (~):

>>> subq = (
...     select(address_table.c.id).where(user_table.c.id == address_table.c.user_id)
... ).exists()
>>> with engine.connect() as conn:
...     result = conn.execute(select(user_table.c.name).where(~subq))
...     print(result.all())
BEGIN  (implicit)
SELECT  user_account.name
FROM  user_account
WHERE  NOT  (EXISTS  (SELECT  address.id
FROM  address
WHERE  user_account.id  =  address.user_id))
[...]  ()
[('patrick',)]
ROLLBACK 
```## 使用 SQL 函数
在本节早些时候介绍的 带 GROUP BY / HAVING 的聚合函数,`func` 对象充当创建新的 `Function` 对象的工厂,当在像 `select()` 这样的结构中使用时,会产生一个 SQL 函数显示,通常由名称、一些括号(虽然不总是),以及可能的一些参数组成。典型的 SQL 函数示例包括:
+   `count()` 函数,计算返回的行数的聚合函数:
    ```py
    >>> print(select(func.count()).select_from(user_table))
    SELECT  count(*)  AS  count_1
    FROM  user_account 
    ```
+   `lower()` 函数,将字符串转换为小写的字符串函数:
    ```py
    >>> print(select(func.lower("A String With Much UPPERCASE")))
    SELECT  lower(:lower_2)  AS  lower_1 
    ```
+   `now()` 函数,提供当前日期和时间;由于这是一个常见的函数,SQLAlchemy 知道如何为每个后端呈现这个函数的不同表现形式,在 SQLite 中使用 CURRENT_TIMESTAMP 函数:
    ```py
    >>> stmt = select(func.now())
    >>> with engine.connect() as conn:
    ...     result = conn.execute(stmt)
    ...     print(result.all())
    BEGIN  (implicit)
    SELECT  CURRENT_TIMESTAMP  AS  now_1
    [...]  ()
    [(datetime.datetime(...),)]
    ROLLBACK 
    ```
由于大多数数据库后端都具有几十甚至上百种不同的 SQL 函数,`func` 尽可能宽松地接受任何输入。从这个命名空间访问的任何名称都自动被视为是一个 SQL 函数,将以一种通用的方式呈现:
```py
>>> print(select(func.some_crazy_function(user_table.c.name, 17)))
SELECT  some_crazy_function(user_account.name,  :some_crazy_function_2)  AS  some_crazy_function_1
FROM  user_account 

与此同时,一组相对较小的极其常见的 SQL 函数,如countnowmaxconcat 包括它们自己的预打包版本,这些版本提供了适当的类型信息,并在某些情况下提供特定于后端的 SQL 生成。下面的示例对比了 PostgreSQL 方言和 Oracle 方言对 now 函数的 SQL 生成:

>>> from sqlalchemy.dialects import postgresql
>>> print(select(func.now()).compile(dialect=postgresql.dialect()))
SELECT  now()  AS  now_1
>>> from sqlalchemy.dialects import oracle
>>> print(select(func.now()).compile(dialect=oracle.dialect()))
SELECT  CURRENT_TIMESTAMP  AS  now_1  FROM  DUAL 

函数具有返回类型

由于函数是列表达式,它们还有 SQL 数据类型,描述了生成的 SQL 表达式的数据类型。我们在这里将这些类型称为“SQL 返回类型”,指的是在数据库端 SQL 表达式上下文中由函数返回的 SQL 值的类型,而不是 Python 函数的“返回类型”。

任何 SQL 函数的 SQL 返回类型可以通过引用 Function.type 属性来访问,通常用于调试目的:

>>> func.now().type
DateTime()

这些 SQL 返回类型在将函数表达式用于更大表达式的上下文中时很重要;也就是说,数学运算符在表达式的数据类型为IntegerNumeric之类时效果更佳,为了使 JSON 访问器能够工作,需要使用诸如JSON之类的类型。某些类别的函数返回整行而不是列值,需要引用特定列;这些函数被称为 table valued functions。

在执行语句并获取行时,函数的 SQL 返回类型也可能很重要,特别是对于那些 SQLAlchemy 必须应用结果集处理的情况。一个典型的例子是 SQLite 上的日期相关函数,其中 SQLAlchemy 的DateTime和相关数据类型在收到结果行时扮演了将字符串值转换为 Python datetime()对象的角色。

要将特定类型应用于我们创建的函数,我们使用Function.type_参数传递它;类型参数可以是TypeEngine类或实例。在下面的示例中,我们传递JSON类以生成 PostgreSQL 的json_object()函数,注意 SQL 返回类型将是 JSON 类型:

>>> from sqlalchemy import JSON
>>> function_expr = func.json_object('{a, 1, b, "def", c, 3.5}', type_=JSON)

通过使用带有JSON数据类型的 JSON 函数,SQL 表达式对象具有了与 JSON 相关的功能,例如访问元素:

>>> stmt = select(function_expr["def"])
>>> print(stmt)
SELECT  json_object(:json_object_1)[:json_object_2]  AS  anon_1 

内置函数具有预配置的返回类型

对于像countmaxmin等常见的聚合函数,以及非常少数的日期函数,比如now和字符串函数,比如concat,SQL 返回类型将适当地设置,有时是基于用法。max函数和类似的聚合过滤函数将根据给定的参数设置 SQL 返回类型:

>>> m1 = func.max(Column("some_int", Integer))
>>> m1.type
Integer()
>>> m2 = func.max(Column("some_str", String))
>>> m2.type
String()

日期和时间函数通常对应于由DateTimeDateTime描述的 SQL 表达式:

>>> func.now().type
DateTime()
>>> func.current_date().type
Date()

已知的字符串函数,如concat,将知道 SQL 表达式的类型为String

>>> func.concat("x", "y").type
String()

但是,对于绝大多数 SQL 函数,SQLAlchemy 并没有将它们显式地列在已知函数的非常小的列表中。例如,虽然通常使用 SQL 函数 func.lower()func.upper() 来转换字符串的大小写没有问题,但 SQLAlchemy 实际上并不知道这些函数,因此它们具有“null”SQL 返回类型:

>>> func.upper("lowercase").type
NullType()

对于像upperlower这样的简单函数,通常情况下问题不是很严重,因为字符串值可以在没有任何特殊类型处理的情况下从数据库接收,而且 SQLAlchemy 的类型转换规则通常也能够正确猜测意图;例如,Python 的+操作符会根据表达式的两边正确解释为字符串连接操作符:

>>> print(select(func.upper("lowercase") + " suffix"))
SELECT  upper(:upper_1)  ||  :upper_2  AS  anon_1 

总的来说,Function.type_ 参数可能是必要的情况是:

  1. 如果函数不是 SQLAlchemy 的内置函数;这可以通过创建函数并观察Function.type属性来证明,即:
>>> func.count().type
Integer()
  1. 与:
>>> func.json_object('{"a", "b"}').type
NullType()
  1. 需要支持函数感知的表达式;这通常是指与诸如JSONARRAY之类的数据类型相关的特殊操作符
  2. 需要结果值处理,其中可能包括诸如DateTimeBooleanEnum或者再次是特殊的数据类型,如JSONARRAY

高级 SQL 函数技术

以下各小节说明了可以使用 SQL 函数做的更多事情。虽然这些技术比基本的 SQL 函数使用更不常见且更高级,但它们仍然非常受欢迎,这在很大程度上是由于 PostgreSQL 强调更复杂的函数形式,包括与 JSON 数据流行的表和列值形式。

使用窗口函数

窗口函数是 SQL 聚合函数的特殊用法,它在处理个别结果行时计算在一组中返回的行上的聚合值。而像 MAX() 这样的函数将为你提供一组行中的列的最高值,使用相同函数作为“窗口函数”将为你提供每行的最高值,截至该行

在 SQL 中,窗口函数允许指定应该应用函数的行、一个考虑不同行子集的“分区”值以及一个重要的指示行应该应用到聚合函数的顺序的“order by”表达式。

在 SQLAlchemy 中,由 func 命名空间生成的所有 SQL 函数都包括一个 FunctionElement.over() 方法,它授予窗口函数或“OVER”语法;生成的结构是 Over 结构。

与窗口函数一起常用的函数是 row_number() 函数,它简单地计算行数。我们可以根据用户名对此行计数进行分区,以为个别用户的电子邮件地址编号:

>>> stmt = (
...     select(
...         func.row_number().over(partition_by=user_table.c.name),
...         user_table.c.name,
...         address_table.c.email_address,
...     )
...     .select_from(user_table)
...     .join(address_table)
... )
>>> with engine.connect() as conn:  
...     result = conn.execute(stmt)
...     print(result.all())
BEGIN  (implicit)
SELECT  row_number()  OVER  (PARTITION  BY  user_account.name)  AS  anon_1,
user_account.name,  address.email_address
FROM  user_account  JOIN  address  ON  user_account.id  =  address.user_id
[...]  ()
[(1, 'sandy', 'sandy@sqlalchemy.org'), (2, 'sandy', 'sandy@squirrelpower.org'), (1, 'spongebob', 'spongebob@sqlalchemy.org')]
ROLLBACK 

上文中,使用了 FunctionElement.over.partition_by 参数,以使 PARTITION BY 子句在 OVER 子句中呈现。我们还可以使用 FunctionElement.over.order_by 来使用 ORDER BY 子句:

>>> stmt = (
...     select(
...         func.count().over(order_by=user_table.c.name),
...         user_table.c.name,
...         address_table.c.email_address,
...     )
...     .select_from(user_table)
...     .join(address_table)
... )
>>> with engine.connect() as conn:  
...     result = conn.execute(stmt)
...     print(result.all())
BEGIN  (implicit)
SELECT  count(*)  OVER  (ORDER  BY  user_account.name)  AS  anon_1,
user_account.name,  address.email_address
FROM  user_account  JOIN  address  ON  user_account.id  =  address.user_id
[...]  ()
[(2, 'sandy', 'sandy@sqlalchemy.org'), (2, 'sandy', 'sandy@squirrelpower.org'), (3, 'spongebob', 'spongebob@sqlalchemy.org')]
ROLLBACK 

窗口函数的更多选项包括使用范围;请参阅 over() 以获取更多示例。

提示

需要注意的是,FunctionElement.over() 方法仅适用于那些实际上是聚合函数的 SQL 函数;虽然 Over 结构会愉快地为任何给定的 SQL 函数渲染自己,但如果函数本身不是 SQL 聚合函数,数据库将拒绝该表达式。 #### 特殊修饰符 WITHIN GROUP, FILTER

“WITHIN GROUP” SQL 语法与“有序集合”或“假设集合”聚合函数一起使用。常见的“有序集合”函数包括percentile_cont()rank()。SQLAlchemy 包含内置实现rank, dense_rank, mode, percentile_contpercentile_disc,其中包括一个 FunctionElement.within_group() 方法:

>>> print(
...     func.unnest(
...         func.percentile_disc([0.25, 0.5, 0.75, 1]).within_group(user_table.c.name)
...     )
... )
unnest(percentile_disc(:percentile_disc_1)  WITHIN  GROUP  (ORDER  BY  user_account.name)) 

“FILTER” 受一些后端支持,用于将聚合函数的范围限制为与返回的总行范围相比的特定子集,可使用 FunctionElement.filter() 方法:

>>> stmt = (
...     select(
...         func.count(address_table.c.email_address).filter(user_table.c.name == "sandy"),
...         func.count(address_table.c.email_address).filter(
...             user_table.c.name == "spongebob"
...         ),
...     )
...     .select_from(user_table)
...     .join(address_table)
... )
>>> with engine.connect() as conn:  
...     result = conn.execute(stmt)
...     print(result.all())
BEGIN  (implicit)
SELECT  count(address.email_address)  FILTER  (WHERE  user_account.name  =  ?)  AS  anon_1,
count(address.email_address)  FILTER  (WHERE  user_account.name  =  ?)  AS  anon_2
FROM  user_account  JOIN  address  ON  user_account.id  =  address.user_id
[...]  ('sandy',  'spongebob')
[(2, 1)]
ROLLBACK 
```#### 表值函数
表值 SQL 函数支持包含命名子元素的标量表示形式。通常用于 JSON 和 ARRAY 导向的函数以及像`generate_series()`这样的函数,表值函数在 FROM 子句中指定,然后被引用为表,有时甚至作为列。这种形式的函数在 PostgreSQL 数据库中非常突出,但某些形式的表值函数也受 SQLite、Oracle 和 SQL Server 支持。
另请参阅
表值、表和列值函数、行和元组对象 - 在 PostgreSQL 文档中。
虽然许多数据库支持表值和其他特殊形式,但 PostgreSQL 往往是对这些功能需求最大的地方。请参阅本节,了解 PostgreSQL 语法的附加示例以及其他功能。
SQLAlchemy 提供了 `FunctionElement.table_valued()` 方法作为基本的“表值函数”构造,它将一个 `func` 对象转换为一个包含一系列命名列的 FROM 子句,这些列是基于按位置传递的字符串名称。这将返回一个 `TableValuedAlias` 对象,它是一个启用函数的 `Alias` 构造,可像在 使用别名 中介绍的其他 FROM 子句一样使用。下面我们举例说明 `json_each()` 函数,尽管在 PostgreSQL 上很常见,但也受到现代版本的 SQLite 的支持:
```py
>>> onetwothree = func.json_each('["one", "two", "three"]').table_valued("value")
>>> stmt = select(onetwothree).where(onetwothree.c.value.in_(["two", "three"]))
>>> with engine.connect() as conn:
...     result = conn.execute(stmt)
...     result.all()
BEGIN  (implicit)
SELECT  anon_1.value
FROM  json_each(?)  AS  anon_1
WHERE  anon_1.value  IN  (?,  ?)
[...]  ('["one", "two", "three"]',  'two',  'three')
[('two',), ('three',)]
ROLLBACK 

在上面的例子中,我们使用了 SQLite 和 PostgreSQL 支持的 json_each() JSON 函数来生成一个具有单列(称为 value)的表值表达式,并选择了其三行中的两行。

另请参阅

表值函数 - 在 PostgreSQL 文档中 - 此部分将详细介绍其他语法,例如特殊列派生和“WITH ORDINALITY”,已知可与 PostgreSQL 一起使用。

PostgreSQL 和 Oracle 支持的特殊语法是在 FROM 子句中引用函数,然后将其自身作为 SELECT 语句或其他列表达式上的列传递到列子句中。 PostgreSQL 在 json_array_elements()json_object_keys()json_each_text()json_each() 等函数中广泛使用此语法。

SQLAlchemy 将其称为“列值函数”,可通过将 FunctionElement.column_valued() 修饰符应用于 Function 构造来使用:

>>> from sqlalchemy import select, func
>>> stmt = select(func.json_array_elements('["one", "two"]').column_valued("x"))
>>> print(stmt)
SELECT  x
FROM  json_array_elements(:json_array_elements_1)  AS  x 

“列值形式”也受到 Oracle 方言的支持,可以用于自定义 SQL 函数:

>>> from sqlalchemy.dialects import oracle
>>> stmt = select(func.scalar_strings(5).column_valued("s"))
>>> print(stmt.compile(dialect=oracle.dialect()))
SELECT  s.COLUMN_VALUE
FROM  TABLE  (scalar_strings(:scalar_strings_1))  s 

另请参阅

列值函数 - 在 PostgreSQL 文档中。 ## 数据转换和类型强制

在 SQL  中,我们经常需要明确指定表达式的数据类型,要么是为了告诉数据库在一个否则模棱两可的表达式中期望的类型是什么,要么是在某些情况下,当我们想要将  SQL 表达式的隐含数据类型转换为其他内容时。SQL CAST 关键字用于此任务,在 SQLAlchemy 中由cast()函数提供。该函数接受列表达式和数据类型对象作为参数,如下所示,我们从user_table.c.id列对象生成一个 SQL 表达式CAST(user_account.id AS VARCHAR)

>>> from sqlalchemy import cast
>>> stmt = select(cast(user_table.c.id, String))
>>> with engine.connect() as conn:
...     result = conn.execute(stmt)
...     result.all()
BEGIN  (implicit)
SELECT  CAST(user_account.id  AS  VARCHAR)  AS  id
FROM  user_account
[...]  ()
[('1',), ('2',), ('3',)]
ROLLBACK 

cast()函数不仅会渲染 SQL CAST 语法,还会生成一个 SQLAlchemy 列表达式,在 Python 端也将作为给定的数据类型。将字符串表达式cast()JSON将获得 JSON 下标和比较运算符,例如:

>>> from sqlalchemy import JSON
>>> print(cast("{'a': 'b'}", JSON)["a"])
CAST(:param_1  AS  JSON)[:param_2] 

type_coerce() - 一个仅限于 Python 的“类型转换”函数

有时需要让 SQLAlchemy 知道表达式的数据类型,出于前述所有原因,但是不要在 SQL 端渲染 CAST 表达式本身,因为它可能会干扰已经正常工作的 SQL 操作。对于这种相当常见的用例,有另一个函数type_coerce(),它与cast()密切相关,它将设置一个 Python 表达式为具有特定 SQL 数据库类型,但不会在数据库端渲染 CAST 关键字或数据类型。当处理JSON数据类型时,type_coerce()特别重要,它通常与不同平台上的字符串定向数据类型有着错综复杂的关系,甚至可能不是一个显式的数据类型,例如在 SQLite 和 MariaDB 上。下面,我们使用type_coerce()将一个 Python 结构作为 JSON 字符串传递给 MySQL 的一个 JSON 函数:

>>> import json
>>> from sqlalchemy import JSON
>>> from sqlalchemy import type_coerce
>>> from sqlalchemy.dialects import mysql
>>> s = select(type_coerce({"some_key": {"foo": "bar"}}, JSON)["some_key"])
>>> print(s.compile(dialect=mysql.dialect()))
SELECT  JSON_EXTRACT(%s,  %s)  AS  anon_1 

在上面的例子中,调用了 MySQL 的 JSON_EXTRACT SQL 函数,因为我们使用 type_coerce() 指示我们的 Python 字典应该被视为 JSON。Python 的 __getitem__ 运算符,在这种情况下,['some_key'] 变得可用,并允许一个 JSON_EXTRACT 路径表达式(但在本例中没有显示,最终将是 '$."some_key"')被渲染。

选择(select())SQL 表达式构造

select() 构造以与 insert() 相同的方式构建语句,使用一种生成式方法,其中每个方法都向对象添加更多状态。与其他 SQL 构造一样,它可以在原地转换为字符串:

>>> from sqlalchemy import select
>>> stmt = select(user_table).where(user_table.c.name == "spongebob")
>>> print(stmt)
SELECT  user_account.id,  user_account.name,  user_account.fullname
FROM  user_account
WHERE  user_account.name  =  :name_1 

同样,与所有其他语句级 SQL 构造一样,要实际运行语句,我们将其传递给执行方法。由于 SELECT 语句返回行,我们总是可以迭代结果对象以获取 Row 对象:

>>> with engine.connect() as conn:
...     for row in conn.execute(stmt):
...         print(row)
BEGIN  (implicit)
SELECT  user_account.id,  user_account.name,  user_account.fullname
FROM  user_account
WHERE  user_account.name  =  ?
[...]  ('spongebob',)
(1, 'spongebob', 'Spongebob Squarepants')
ROLLBACK 

当使用 ORM,特别是对 ORM 实体组成的 select() 构造进行操作时,我们将希望使用 Session.execute() 方法执行它;使用这种方法,我们仍然从结果中获取 Row 对象,但是这些行现在可以包括完整的实体,例如 User 类的实例,作为每一行中的单独元素:

>>> stmt = select(User).where(User.name == "spongebob")
>>> with Session(engine) as session:
...     for row in session.execute(stmt):
...         print(row)
BEGIN  (implicit)
SELECT  user_account.id,  user_account.name,  user_account.fullname
FROM  user_account
WHERE  user_account.name  =  ?
[...]  ('spongebob',)
(User(id=1, name='spongebob', fullname='Spongebob Squarepants'),)
ROLLBACK 

下面的章节将更详细地讨论 SELECT 构造。


SqlAlchemy 2.0 中文文档(二)(3)https://developer.aliyun.com/article/1560325

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
4月前
|
SQL 测试技术 Python
SqlAlchemy 2.0 中文文档(四)(4)
SqlAlchemy 2.0 中文文档(四)
54 3
|
4月前
|
SQL 存储 API
SqlAlchemy 2.0 中文文档(四)(3)
SqlAlchemy 2.0 中文文档(四)
44 3
|
4月前
|
SQL 存储 数据库
SqlAlchemy 2.0 中文文档(一)(4)
SqlAlchemy 2.0 中文文档(一)
69 1
|
4月前
|
SQL 数据库 数据库管理
SqlAlchemy 2.0 中文文档(一)(2)
SqlAlchemy 2.0 中文文档(一)
131 1
|
4月前
|
SQL 存储 API
SqlAlchemy 2.0 中文文档(十)(5)
SqlAlchemy 2.0 中文文档(十)
32 1
|
4月前
|
SQL 关系型数据库 测试技术
SqlAlchemy 2.0 中文文档(十)(1)
SqlAlchemy 2.0 中文文档(十)
30 1
|
4月前
|
测试技术 API 数据库
SqlAlchemy 2.0 中文文档(十)(4)
SqlAlchemy 2.0 中文文档(十)
64 1
|
4月前
|
SQL 缓存 关系型数据库
SqlAlchemy 2.0 中文文档(三)(2)
SqlAlchemy 2.0 中文文档(三)
33 1
|
4月前
|
SQL 测试技术 Python
SqlAlchemy 2.0 中文文档(二)(1)
SqlAlchemy 2.0 中文文档(二)
54 2
|
4月前
|
存储 Python
SqlAlchemy 2.0 中文文档(七)(5)
SqlAlchemy 2.0 中文文档(七)
26 1