使用核心或 ORM 选择行

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

SQLAlchemy 1.4 / 2.0 Tutorial

此页是 SQLAlchemy 1.4/2.0教程

上一次: 插入带核心的行 |下一步: |next|

使用核心或ORM选择行

对于核心和ORM, select() 函数会生成一个 Select 用于所有SELECT查询的构造。传递给像这样的方法 Connection.execute() 在核心和 Session.execute() 在ORM中,在当前事务中发出SELECT语句,结果行通过返回的 Result 对象。

ORM阅读器 -这里的内容同样适用于Core和ORM的使用,这里提到了基本的ORM变体用例。但是,还有更多特定于ORM的特性可用;这些特性在 ORM查询指南

SELECT()SQL表达式构造

这个 select() 构造以与构造语句相同的方式构建语句 insert() ,使用 generative 一种方法,其中每个方法在对象上构建更多的状态。与其他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时,特别是在使用 select() 构造针对ORM实体组成,我们将希望使用 Session.execute() 方法的基础上。 Session ;使用这种方法,我们继续获得 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

从表与ORM类中选择()

虽然在这些示例中生成的SQL看起来是一样的,但是无论我们调用 select(user_table)select(User) 在更一般的情况下,它们不一定呈现相同的东西,因为ORM映射的类可以映射到除表之外的其他类型的“可选择”。这个 select() 这是针对ORM实体的,它还指示应该在结果中返回ORM映射的实例,而在从 Table 对象。

以下各节将更详细地讨论SELECT结构。

设置COLUMNS和FROM子句

这个 select() 函数接受表示任意数量的位置元素的 Column 和/或 Table 表达式以及范围广泛的兼容对象,这些对象被解析为要从中选择的SQL表达式列表,该列表将作为结果集中的列返回。在更简单的情况下,这些元素还用于创建FROM子句,该子句是从传递的列和类似表的表达式中推断出来的::

>>> print(select(user_table))
SELECT user_account.id, user_account.name, user_account.fullname
FROM user_account

要使用核心方法从各个列中选择, Column 对象是从 Table.c 访问器,并且可以直接发送;FROM子句将被推断为所有 Table 以及其他 FromClause 由以下列表示的对象::

>>> print(select(user_table.c.name, user_table.c.fullname))
SELECT user_account.name, user_account.fullname
FROM user_account

选择ORM实体和列

ORM实体,比如我们的 User 类以及其上的列映射属性(如 User.name ,还参与了表示表和列的SQL表达式语言系统。下面演示了一个从 User 实体,它最终呈现的方式与我们使用 user_table 直接::

>>> print(select(User))
SELECT user_account.id, user_account.name, user_account.fullname
FROM user_account

在使用ORM执行类似上面的语句时 Session.execute() 方法,当我们从完整的实体(如 User ,而不是 user_table ,这就是说, 实体本身作为每行中的单个元素返回 。也就是说,当我们从上面的语句提取行时,因为只有 User 实体在要获取的物品列表中,我们返回 Row 只有一个元素的对象,这些元素包含 User 班级::

>>> row = session.execute(select(User)).first()
BEGIN...
SELECT user_account.id, user_account.name, user_account.fullname
FROM user_account
[...] ()
>>> row
(User(id=1, name='spongebob', fullname='Spongebob Squarepants'),)

以上内容 Row 只有一个元素,表示 User 实体::

>>> row[0]
User(id=1, name='spongebob', fullname='Spongebob Squarepants')

或者,通过使用类绑定属性,我们可以选择ORM实体的各个列作为结果行中的不同元素;当这些属性传递给诸如 select() ,则将它们解析为 Column 或由每个属性表示的其他SQL表达式::

>>> print(select(User.name, User.fullname))
SELECT user_account.name, user_account.fullname
FROM user_account

当我们调用 this 语句使用 Session.execute() ,我们现在会收到每个值都有单独元素的行,每个元素对应一个单独的列或其他SQL表达式::

>>> row = session.execute(select(User.name, User.fullname)).first()
SELECT user_account.name, user_account.fullname
FROM user_account
[...] ()
>>> row
('spongebob', 'Spongebob Squarepants')

这些方法也可以混合使用,如下所示,我们在其中选择 name 属性的属性。 User 实体作为行的第一个元素,并将其与FULL组合 Address 第二个元素中的实体::

>>> session.execute(
...     select(User.name, Address).
...     where(User.id==Address.user_id).
...     order_by(Address.id)
... ).all()
SELECT user_account.name, address.id, address.email_address, address.user_id
FROM user_account, address
WHERE user_account.id = address.user_id ORDER BY address.id
[...] ()
[('spongebob', Address(id=1, email_address='spongebob@sqlalchemy.org')),
('sandy', Address(id=2, email_address='sandy@sqlalchemy.org')),
('sandy', Address(id=3, email_address='sandy@squirrelpower.org'))]

中进一步讨论了选择ORM实体和列的方法以及转换行的常用方法 选择ORM实体和属性

参见

选择ORM实体和属性 - in the ORM查询指南

从带标签的SQL表达式中选择

这个 ColumnElement.label() 方法以及ORM属性上可用的同名方法提供列或表达式的SQL标签,从而允许它在结果集中具有特定的名称。在按名称引用结果行中的任意SQL表达式时,这会很有帮助:

>>> from sqlalchemy import func, cast
>>> stmt = (
...     select(
...         ("Username: " + user_table.c.name).label("username"),
...     ).order_by(user_table.c.name)
... )
>>> with engine.connect() as conn:
...     for row in conn.execute(stmt):
...         print(f"{row.username}")
BEGIN (implicit)
SELECT ? || user_account.name AS username
FROM user_account ORDER BY user_account.name
[...] ('Username: ',)
Username: patrick
Username: sandy
Username: spongebob
ROLLBACK

参见

按标签排序或分组 -我们创建的标签名称也可以在的ORDER BY或GROUP BY子句中引用 Select

使用文本列表达式选择

当我们构造一个 Select 对象使用 select() 函数,我们通常会向它传递一系列 TableColumn 使用定义的对象 table metadata ,或者在使用ORM时,我们可能会发送表示表列的ORM映射属性。但是,有时也需要在语句中制造任意的SQL块,比如常量字符串表达式,或者只是一些更快编写的任意SQL。

这个 text() 在以下位置介绍的构造 处理事务和DBAPI 实际上可以嵌入到 Select 直接构造,如下面我们制造硬编码字符串文字的地方 'some label' 并将其嵌入到SELECT语句中::

>>> from sqlalchemy import text
>>> stmt = (
...     select(
...         text("'some phrase'"), user_table.c.name
...     ).order_by(user_table.c.name)
... )
>>> with engine.connect() as conn:
...     print(conn.execute(stmt).all())
BEGIN (implicit)
SELECT 'some phrase', user_account.name
FROM user_account ORDER BY user_account.name
[generated in ...] ()
[('some phrase', 'patrick'), ('some phrase', 'sandy'), ('some phrase', 'spongebob')]
ROLLBACK

在此期间, text() 构造可以在大多数地方用来注入文字SQL短语,我们通常实际处理的是文本单元,每个文本单元代表一个单独的列表达式。在这种常见情况下,我们可以使用 literal_column() 代之以构造。此对象类似于 text() 不同的是,它不是表示任何形式的任意SQL,而是显式表示单个“列”,然后可以在子查询和其他表达式中对其进行标记和引用:

>>> from sqlalchemy import literal_column
>>> stmt = (
...     select(
...         literal_column("'some phrase'").label("p"), user_table.c.name
...     ).order_by(user_table.c.name)
... )
>>> with engine.connect() as conn:
...     for row in conn.execute(stmt):
...         print(f"{row.p}, {row.name}")
BEGIN (implicit)
SELECT 'some phrase' AS p, user_account.name
FROM user_account ORDER BY user_account.name
[generated in ...] ()
some phrase, patrick
some phrase, sandy
some phrase, spongebob
ROLLBACK

请注意,在这两种情况下,当使用 text()literal_column() ,我们编写的是语法SQL表达式,而不是文字值。因此,我们必须包括我们希望看到的呈现SQL所需的任何引用或语法。

WHERE子句

SQLAlChemy允许我们编写SQL表达式,例如 name = 'squidward' or user_id > 10, by making use of standard Python operators in conjunction with Column and similar objects. For boolean expressions, most Python operators such as ==, !=, <, >= etc. generate new SQL Expression objects, rather than plain boolean True/False 值::

>>> print(user_table.c.name == 'squidward')
user_account.name = :name_1

>>> print(address_table.c.user_id > 10)
address.user_id > :user_id_1

我们可以使用这样的表达式来生成WHERE子句,方法是将生成的对象传递给 Select.where() 方法:

>>> print(select(user_table).where(user_table.c.name == 'squidward'))
SELECT user_account.id, user_account.name, user_account.fullname
FROM user_account
WHERE user_account.name = :name_1

若要生成由和连接的多个表达式, Select.where() 方法可以被调用任意次::

>>> print(
...     select(address_table.c.email_address).
...     where(user_table.c.name == 'squidward').
...     where(address_table.c.user_id == user_table.c.id)
... )
SELECT address.email_address
FROM address, user_account
WHERE user_account.name = :name_1 AND address.user_id = user_account.id

一次呼叫 Select.where() 还接受具有相同效果的多个表达式::

>>> print(
...     select(address_table.c.email_address).
...     where(
...          user_table.c.name == 'squidward',
...          address_table.c.user_id == user_table.c.id
...     )
... )
SELECT address.email_address
FROM address, user_account
WHERE user_account.name = :name_1 AND address.user_id = user_account.id

“AND”和“OR”连词都可以直接使用 and_()or_() 函数,如下所示的ORM实体:

>>> from sqlalchemy import and_, or_
>>> print(
...     select(Address.email_address).
...     where(
...         and_(
...             or_(User.name == 'squidward', User.name == 'sandy'),
...             Address.user_id == User.id
...         )
...     )
... )
SELECT address.email_address
FROM address, user_account
WHERE (user_account.name = :name_1 OR user_account.name = :name_2)
AND address.user_id = user_account.id

对于针对单个实体的简单“相等”比较,还有一种流行的方法,称为 Select.filter_by() 它接受与列键或ORM属性名称匹配的关键字参数。它将对最左边的FROM子句或最后加入的实体使用过滤::

>>> print(
...     select(User).filter_by(name='spongebob', fullname='Spongebob Squarepants')
... )
SELECT user_account.id, user_account.name, user_account.fullname
FROM user_account
WHERE user_account.name = :name_1 AND user_account.fullname = :fullname_1

参见

操作员参考 -SQLAlChemy中大多数SQL运算符函数的说明

显式FROM子句和连接

如前所述,FROM子句通常是 推论 基于我们在COLUMNS子句中设置的表达式以及 Select

如果我们从特定的 Table 在COLUMNS子句中,它将 Table 在FROM子句中也是如此::

>>> print(select(user_table.c.name))
SELECT user_account.name
FROM user_account

如果我们放入两个表中的列,则会得到一个逗号分隔的FROM子句::

>>> print(select(user_table.c.name, address_table.c.email_address))
SELECT user_account.name, address.email_address
FROM user_account, address

为了将这两个表联接在一起,我们通常在 Select 。第一个是 Select.join_from() 方法,该方法允许我们显式地指示联接的左侧和右侧:

>>> print(
...     select(user_table.c.name, address_table.c.email_address).
...     join_from(user_table, address_table)
... )
SELECT user_account.name, address.email_address
FROM user_account JOIN address ON user_account.id = address.user_id

另一种是 Select.join() 方法,该方法仅指示联接的右侧,则推断左侧::

>>> print(
...     select(user_table.c.name, address_table.c.email_address).
...     join(address_table)
... )
SELECT user_account.name, address.email_address
FROM user_account JOIN address ON user_account.id = address.user_id

如果FROM子句没有按照我们希望的方式从COLUMNS子句中推断出来,我们还可以选择显式地将元素添加到FROM子句中。我们使用 Select.select_from() 方法来实现此目的,如下所示,我们在下面的位置建立 user_table 作为FROM子句中的第一个元素,并且 Select.join() 建立 address_table 作为第二个::

>>> print(
...     select(address_table.c.email_address).
...     select_from(user_table).join(address_table)
... )
SELECT address.email_address
FROM user_account JOIN address ON user_account.id = address.user_id

我们可能想要使用的另一个示例 Select.select_from() 如果我们的COLUMNS子句没有足够的信息来提供FROM子句。例如,要从通用SQL表达式中选择 count(*) 中,我们使用SQLAlChemy元素,称为 sqlalchemy.sql.expression.func 要生成SQL,请执行以下操作 count() 功能::

>>> from sqlalchemy import func
>>> print (
...     select(func.count('*')).select_from(user_table)
... )
SELECT count(:count_2) AS count_1
FROM user_account

参见

控制加入内容 -在 ORM查询指南 -包含有关交互的其他示例和注释 Select.select_from()Select.join()

设置ON子句

前面的连接示例说明了 Select 构造可以在两个表之间连接并自动生成ON子句。在这些示例中会出现这种情况,因为 user_tableaddress_table Table 对象包括单个 ForeignKeyConstraint 用于形成此ON子句的定义。

如果联接的左目标和右目标没有这样的约束,或者存在多个约束,我们需要直接指定ON子句。两者都有 Select.join()Select.join_from() 接受ON子句的另一个参数,它使用与我们在中看到的相同的SQL表达式机制来声明 WHERE子句 ::

>>> print(
...     select(address_table.c.email_address).
...     select_from(user_table).
...     join(address_table, user_table.c.id == address_table.c.user_id)
... )
SELECT address.email_address
FROM user_account JOIN address ON user_account.id = address.user_id

ORM提示 -当使用ORM实体时,还有另一种生成ON子句的方法,这些ORM实体利用 relationship() 构造,就像上一节中在 声明映射类 。这是一个完整的主题,详细介绍在 使用关系加入

外部连接和完全连接

这两个 Select.join()Select.join_from() 方法接受关键字参数 Select.join.isouterSelect.join.full 这将分别呈现左外部联接和完全外部联接::

>>> print(
...     select(user_table).join(address_table, isouter=True)
... )
SELECT user_account.id, user_account.name, user_account.fullname
FROM user_account LEFT OUTER JOIN address ON user_account.id = address.user_id
>>> print(
...     select(user_table).join(address_table, full=True)
... )
SELECT user_account.id, user_account.name, user_account.fullname
FROM user_account FULL OUTER JOIN address ON user_account.id = address.user_id

还有一种方法 Select.outerjoin() 这相当于使用 .join(..., isouter=True)

小技巧

SQL也有一个“右外联接”。SQLAlChemy不会直接呈现这种情况;相反,可以颠倒表的顺序并使用“LEFT OUTER JOIN”。

ORDER BY、GROUP BY、

SELECT SQL语句包括一个名为ORDER BY的子句,该子句用于返回给定顺序内的选定行。

GROUP BY子句的构造类似于ORDER BY子句,其目的是将所选行细分为可调用聚合函数的特定组。HAVING子句通常与GROUP BY一起使用,其形式与WHERE子句类似,不同之处在于它应用于组内使用的聚合函数。

排序依据

ORDER BY子句是根据SQL表达式结构构造的,通常基于 Column 或类似的物体。这个 Select.order_by() 方法按位置接受以下一个或多个表达式::

>>> print(select(user_table).order_by(user_table.c.name))
SELECT user_account.id, user_account.name, user_account.fullname
FROM user_account ORDER BY user_account.name

升序/降序可从 ColumnElement.asc()ColumnElement.desc() 修饰符,也来自ORM绑定属性::

>>> print(select(User).order_by(User.fullname.desc()))
SELECT user_account.id, user_account.name, user_account.fullname
FROM user_account ORDER BY user_account.fullname DESC

上面的语句将生成按 user_account.fullname 列按降序排列。

使用GROUP BY/HAVING聚合函数

在SQL中,聚合函数允许将多行中的列表达式聚合在一起以生成单个结果。示例包括计数、计算平均值以及在一组值中定位最大值或最小值。

SQLAlChemy使用名为的命名空间以开放式方式提供SQL函数 func 。这是一个特殊的构造函数对象,它将创建 Function 当给定特定SQL函数的名称时,该函数可以具有任何名称,以及要传递给该函数的零个或多个参数,与所有其他情况下一样,这些参数是SQL表达式构造。例如,要针对SQL Count()函数呈现SQL Count()函数,请执行以下操作 user_account.id 列中,我们调用 count() 名称::

>>> from sqlalchemy import func
>>> count_fn = func.count(user_table.c.id)
>>> print(count_fn)
count(user_account.id)

本教程稍后将更详细地介绍SQL函数,网址为 使用SQL函数

在SQL中使用聚合函数时,GROUP BY子句非常重要,因为它允许将行分区为组,其中聚合函数将分别应用于每个组。当在SELECT语句的COLUMNS子句中请求非聚集列时,SQL要求这些列都受GROUP BY子句的约束,无论是直接还是间接基于主键关联。然后,HAVING子句的使用方式与WHERE子句类似,不同之处在于它基于聚合值而不是直接行内容筛选出行。

SQLAlChemy为这两个子句提供了使用 Select.group_by()Select.having() 方法。下面我们将说明如何为那些拥有多个地址的用户选择用户名字段和地址计数:

>>> with engine.connect() as conn:
...     result = conn.execute(
...         select(User.name, func.count(Address.id).label("count")).
...         join(Address).
...         group_by(User.name).
...         having(func.count(Address.id) > 1)
...     )
...     print(result.all())
BEGIN (implicit)
SELECT user_account.name, count(address.id) AS count
FROM user_account JOIN address ON user_account.id = address.user_id GROUP BY user_account.name
HAVING count(address.id) > ?
[...] (1,)
[('sandy', 2)]
ROLLBACK

按标签排序或分组

一项重要的技术,特别是在某些数据库后端上,是能够按COLUMNS子句中已经声明的表达式进行ORDER BY或GROUP BY,而无需在ORDER BY或GROUP BY子句中重新声明表达式,而是使用COLUMNS子句中的列名或标签名。通过将名称的字符串文本传递给 Select.order_by()Select.group_by() 方法。传递的文本为 不是直接渲染的 ;相反,是在COLUMNS子句中为表达式指定的名称,并在上下文中呈现为该表达式名称,如果未找到匹配项,则会引发错误。一元修饰符 asc()desc() 也可以在此表单中使用:

>>> from sqlalchemy import func, desc
>>> stmt = select(
...         Address.user_id,
...         func.count(Address.id).label('num_addresses')).\
...         group_by("user_id").order_by("user_id", desc("num_addresses"))
>>> print(stmt)
SELECT address.user_id, count(address.id) AS num_addresses
FROM address GROUP BY address.user_id ORDER BY address.user_id, num_addresses DESC

使用别名

既然我们正在从多个表中进行选择并使用联接,那么我们很快就会遇到需要在语句的FROM子句中多次引用同一个表的情况。我们使用SQL来实现这一点 别名 ,这是一种语法,它为可以在语句中引用的表或子查询提供替代名称。

在SQLAlChemy表达式语言中,这些“名称”改为由 FromClause 对象,称为 Alias 构造,该构造是在Core中使用 FromClause.alias() 方法。一个 Alias 构造就像一个 Table 构造,因为它还具有 Column 对象中的对象 Alias.c 收藏。例如,下面的SELECT语句返回所有唯一的用户名对:

>>> user_alias_1 = user_table.alias()
>>> user_alias_2 = user_table.alias()
>>> print(
...     select(user_alias_1.c.name, user_alias_2.c.name).
...     join_from(user_alias_1, user_alias_2, user_alias_1.c.id > user_alias_2.c.id)
... )
SELECT user_account_1.name, user_account_2.name AS name_1
FROM user_account AS user_account_1
JOIN user_account AS user_account_2 ON user_account_1.id > user_account_2.id

ORM实体别名

ORM等效于 FromClause.alias() 方法是ORM aliased() 函数,该函数可应用于实体,如 UserAddress 。这会产生一个 Alias 对象,该对象与原始映射的 Table 对象,同时保持ORM功能。下面的选择从 User 对包括两个特定电子邮件地址的所有对象进行实体::

>>> from sqlalchemy.orm import aliased
>>> address_alias_1 = aliased(Address)
>>> address_alias_2 = aliased(Address)
>>> print(
...     select(User).
...     join_from(User, address_alias_1).
...     where(address_alias_1.email_address == 'patrick@aol.com').
...     join_from(User, address_alias_2).
...     where(address_alias_2.email_address == 'patrick@gmail.com')
... )
SELECT user_account.id, user_account.name, user_account.fullname
FROM user_account
JOIN address AS address_1 ON user_account.id = address_1.user_id
JOIN address AS address_2 ON user_account.id = address_2.user_id
WHERE address_1.email_address = :email_address_1
AND address_2.email_address = :email_address_2

小技巧

如中所述 设置ON子句 ,ORM提供了另一种使用 relationship() 构造。上面使用别名的示例使用 relationship() 在… 在别名目标之间连接

子查询和CTE

SQL中的子查询是在括号中呈现并放在封闭语句(通常是SELECT语句,但不一定是SELECT语句)上下文中的SELECT语句。

本节将介绍所谓的“非标量”子查询,它通常放在封闭SELECT的FROM子句中。我们还将介绍公用表表达式(CTE),它的使用方式与子查询类似,但包含其他功能。

SQLAlChemy使用 Subquery 对象来表示子查询,并且 CTE 表示CTE,通常从 Select.subquery()Select.cte() 方法分别采用不同的方法。这两个对象都可以用作较大的 select() 构造。

我们可以构建一个 Subquery 属性中选择行的聚合计数。 address 表(集合函数和GROUP BY在前面的 使用GROUP BY/HAVING聚合函数 ):

>>> subq = select(
...     func.count(address_table.c.id).label("count"),
...     address_table.c.user_id
... ).group_by(address_table.c.user_id).subquery()

仅限定子查询本身,而不将其嵌入到另一个子查询中 Select 或OTHER语句生成不带任何括号的普通SELECT语句::

>>> print(subq)
SELECT count(address.id) AS count, address.user_id
FROM address GROUP BY address.user_id

这个 Subquery 对象的行为与任何其他来自对象的对象(如 Table ,值得注意的是,它包括一个 Subquery.c 它选择的列的命名空间。我们可以使用此命名空间来引用 user_id 列以及我们的自定义标签 count 表达式::

>>> print(select(subq.c.user_id, subq.c.count))
SELECT anon_1.user_id, anon_1.count
FROM (SELECT count(address.id) AS count, address.user_id AS user_id
FROM address GROUP BY address.user_id) AS anon_1

控件中包含的行的选择 subq 对象,我们可以将该对象应用于更大的 Select 将数据联接到 user_account 表::

>>> stmt = select(
...    user_table.c.name,
...    user_table.c.fullname,
...    subq.c.count
... ).join_from(user_table, subq)

>>> print(stmt)
SELECT user_account.name, user_account.fullname, anon_1.count
FROM user_account JOIN (SELECT count(address.id) AS count, address.user_id AS user_id
FROM address GROUP BY address.user_id) AS anon_1 ON user_account.id = anon_1.user_id

为了从……加入 user_accountaddress ,我们利用了 Select.join_from() 方法。如前所述,此连接的ON子句再次 推论 基于外键约束。即使SQL子查询本身没有任何约束,SQLAlChemy也可以对列上表示的约束执行操作,方法是确定 subq.c.user_id 列为 派生的address_table.c.user_id 列,该列确实将外键关系表达回 user_table.c.id 列,然后使用该列生成ON子句。

公用表表达式(CTE)

的用法 CTE SQLAlChemy中的构造实际上与 Subquery 构造被使用。通过更改 Select.subquery() 要使用的方法 Select.cte() 相反,我们可以以相同的方式将结果对象用作FROM元素,但是呈现的SQL是非常不同的通用表表达式语法::

>>> subq = select(
...     func.count(address_table.c.id).label("count"),
...     address_table.c.user_id
... ).group_by(address_table.c.user_id).cte()

>>> stmt = select(
...    user_table.c.name,
...    user_table.c.fullname,
...    subq.c.count
... ).join_from(user_table, subq)

>>> print(stmt)
WITH anon_1 AS
(SELECT count(address.id) AS count, address.user_id AS user_id
FROM address GROUP BY address.user_id)
 SELECT user_account.name, user_account.fullname, anon_1.count
FROM user_account JOIN anon_1 ON user_account.id = anon_1.user_id

这个 CTE Construct还具有以“递归”样式使用的功能,在更复杂的情况下可以由INSERT、UPDATE或DELETE语句的返回子句组成。的文档字符串 CTE 包括有关这些附加模式的详细信息。

在这两种情况下,子查询和CTE都是在SQL级别使用“匿名”名称命名的。在Python代码中,我们根本不需要提供这些名称。对象的对象标识。 SubqueryCTE 实例在呈现时用作对象的语法标识。将在SQL中呈现的名称可以通过将其作为 Select.subquery()Select.cte() 方法。

参见

Select.subquery() -有关子查询的更多详细信息

Select.cte() -CTE示例,包括如何使用递归以及面向DML的CTE

ORM实体子查询/CTE

在ORM中, aliased() 构造可用于关联ORM实体,如我们的 UserAddress 类,具有任何 FromClause 表示行源的概念。上一节 ORM实体别名 说明如何使用 aliased() 要将映射的类与 Alias 其映射的 Table 。在这里,我们举例说明 aliased() 对这两个对象执行相同的操作 Subquery 以及一个 CTE 根据 Select 构造,该构造最终派生自同一映射 Table

下面是一个应用程序的示例 aliased() 发送到 Subquery 构造,以便可以从其行中提取ORM实体。结果显示了一系列的 UserAddress 对象,其中每个对象的数据 Address 对象最终来自对 address 表,而不是直接使用该表:

>>> subq = select(Address).where(~Address.email_address.like('%@aol.com')).subquery()
>>> address_subq = aliased(Address, subq)
>>> stmt = select(User, address_subq).join_from(User, address_subq).order_by(User.id, address_subq.id)
>>> with Session(engine) as session:
...     for user, address in session.execute(stmt):
...         print(f"{user} {address}")
BEGIN (implicit)
SELECT user_account.id, user_account.name, user_account.fullname,
anon_1.id AS id_1, anon_1.email_address, anon_1.user_id
FROM user_account JOIN
(SELECT address.id AS id, address.email_address AS email_address, address.user_id AS user_id
FROM address
WHERE address.email_address NOT LIKE ?) AS anon_1 ON user_account.id = anon_1.user_id
ORDER BY user_account.id, anon_1.id
[...] ('%@aol.com',)
User(id=1, name='spongebob', fullname='Spongebob Squarepants') Address(id=1, email_address='spongebob@sqlalchemy.org')
User(id=2, name='sandy', fullname='Sandy Cheeks') Address(id=2, email_address='sandy@sqlalchemy.org')
User(id=2, name='sandy', fullname='Sandy Cheeks') Address(id=3, email_address='sandy@squirrelpower.org')
ROLLBACK

下面是另一个示例,该示例完全相同,只是它使用了 CTE 改为构造:

>>> cte_obj = select(Address).where(~Address.email_address.like('%@aol.com')).cte()
>>> address_cte = aliased(Address, cte_obj)
>>> stmt = select(User, address_cte).join_from(User, address_cte).order_by(User.id, address_cte.id)
>>> with Session(engine) as session:
...     for user, address in session.execute(stmt):
...         print(f"{user} {address}")
BEGIN (implicit)
WITH anon_1 AS
(SELECT address.id AS id, address.email_address AS email_address, address.user_id AS user_id
FROM address
WHERE address.email_address NOT LIKE ?)
SELECT user_account.id, user_account.name, user_account.fullname,
anon_1.id AS id_1, anon_1.email_address, anon_1.user_id
FROM user_account
JOIN anon_1 ON user_account.id = anon_1.user_id
ORDER BY user_account.id, anon_1.id
[...] ('%@aol.com',)
User(id=1, name='spongebob', fullname='Spongebob Squarepants') Address(id=1, email_address='spongebob@sqlalchemy.org')
User(id=2, name='sandy', fullname='Sandy Cheeks') Address(id=2, email_address='sandy@sqlalchemy.org')
User(id=2, name='sandy', fullname='Sandy Cheeks') Address(id=3, email_address='sandy@squirrelpower.org')
ROLLBACK

标量和相关子查询

标量子查询是恰好返回零行或一行和恰好一列的子查询。然后,子查询在封闭的SELECT语句的COLUMNS或WHERE子句中使用,它与常规子查询的不同之处在于它不在FROM子句中使用。一个 correlated subquery 引用包含在SELECT语句中的表的标量子查询。

SQLAlChemy使用 ScalarSelect 构造,该构造是 ColumnElement 表达式层次结构,与由 Subquery 构造,该构造位于 FromClause 层次结构。

标量子查询通常(但不一定)与集合函数一起使用,前面在 使用GROUP BY/HAVING聚合函数 。标量子查询是通过使用 Select.scalar_subquery() 方法如下所示。它的缺省字符串格式在自身被字符串化时呈现为从两个表中选择的普通SELECT语句:

>>> subq = select(func.count(address_table.c.id)).\
...             where(user_table.c.id == address_table.c.user_id).\
...             scalar_subquery()
>>> print(subq)
(SELECT count(address.id) AS count_1
FROM address, user_account
WHERE user_account.id = address.user_id)

以上内容 subq 对象现在落在 ColumnElement SQL表达式层次结构,因为它可以像任何其他列表达式一样使用::

>>> print(subq == 5)
(SELECT count(address.id) AS count_1
FROM address, user_account
WHERE user_account.id = address.user_id) = :param_1

尽管标量子查询本身呈现了这两个 user_accountaddress 在其FROM子句中单独串行化时,将其嵌入到封闭的 select() 构造处理 user_account 表中的 user_account 表格会自动 相关 ,这意味着它不会在子查询的FROM子句中呈现::

>>> stmt = select(user_table.c.name, subq.label("address_count"))
>>> print(stmt)
SELECT user_account.name, (SELECT count(address.id) AS count_1
FROM address
WHERE user_account.id = address.user_id) AS address_count
FROM user_account

简单的相关子查询通常会执行所需的正确操作。但是,在关联不明确的情况下,SQLAlChemy会让我们知道需要更加清晰:

>>> stmt = select(
...     user_table.c.name,
...     address_table.c.email_address,
...     subq.label("address_count")
... ).\
... join_from(user_table, address_table).\
... order_by(user_table.c.id, address_table.c.id)
>>> print(stmt)
Traceback (most recent call last):
...
InvalidRequestError: Select statement '<... Select object at ...>' returned
no FROM clauses due to auto-correlation; specify correlate(<tables>) to
control correlation manually.

要指定 user_table 是我们寻求关联的对象,我们使用 ScalarSelect.correlate()ScalarSelect.correlate_except() 方法:

>>> subq = select(func.count(address_table.c.id)).\
...             where(user_table.c.id == address_table.c.user_id).\
...             scalar_subquery().correlate(user_table)

然后,该语句可以像返回任何其他列一样返回该列的数据:

>>> with engine.connect() as conn:
...     result = conn.execute(
...         select(
...             user_table.c.name,
...             address_table.c.email_address,
...             subq.label("address_count")
...         ).
...         join_from(user_table, address_table).
...         order_by(user_table.c.id, address_table.c.id)
...     )
...     print(result.all())
BEGIN (implicit)
SELECT user_account.name, address.email_address, (SELECT count(address.id) AS count_1
FROM address
WHERE user_account.id = address.user_id) AS address_count
FROM user_account JOIN address ON user_account.id = address.user_id ORDER BY user_account.id, address.id
[...] ()
[('spongebob', 'spongebob@sqlalchemy.org', 1), ('sandy', 'sandy@sqlalchemy.org', 2),
 ('sandy', 'sandy@squirrelpower.org', 2)]
ROLLBACK

UNION、UNION ALL和其他集合运算

在SQL中,可以使用UNION或UNION ALL SQL操作将SELECT语句合并在一起,这将生成一条或多条语句一起生成的所有行的集合。其他集合操作,如交集 [ALL] 而且除了 [ALL] 也是可能的。

SQLAlChemy的 Select Construct使用如下函数支持这种性质的组合 union()intersect()except_() ,以及“所有”对应项 union_all()intersect_all()except_all() 。这些函数都接受任意数量的子选择,它们通常是 Select 构造,但也可以是现有的合成。

由这些函数生成的构造是 CompoundSelect ,它的使用方式与 Select 构造,只是它的方法较少。这个 CompoundSelect 制作人 union_all() 例如,可以使用以下命令直接调用 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() 方法,该方法将产生 Subquery 对象,并使用 FromClause.c 集合,该集合可以在随附的 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

存在的子查询

SQL EXISTS关键字是与一起使用的运算符 scalar subqueries 返回布尔值TRUE或FALSE,具体取决于SELECT语句是否返回一行。SQLAlChemy包括 ScalarSelect 调用的对象 Exists ,它将生成Existes子查询,最方便的生成方式是使用 SelectBase.exists() 方法。下面我们生成一个存在,这样我们就可以返回 user_account 中有多个相关行的行 address

>>> 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() 函数,一个计算返回行数的聚合函数:

    >>> print(select(func.count()).select_from(user_table))
    SELECT count(*) AS count_1
    FROM user_account
  • 这个 lower() 函数,将字符串转换为小写的字符串函数:

    >>> print(select(func.lower("A String With Much UPPERCASE")))
    SELECT lower(:lower_2) AS lower_1
  • 这个 now() 函数,该函数提供当前日期和时间;由于这是一个常见的函数,因此SQLAlChemy知道如何为每个后端以不同的方式呈现它,在SQLite中使用CURRENT_TIMESTAMP函数:

    >>> 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函数::

>>> 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方言的SQL生成与Oracle方言的SQL生成进行了对比 now 功能::

>>> 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 datatypes 描述生成的SQL表达式的数据类型的。我们在这里将这些类型称为“SQL返回类型”,指的是数据库端SQL表达式上下文中函数返回的SQL值的类型,而不是Python函数的“返回类型”。

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

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

在较大表达式的上下文中使用函数表达式时,这些SQL返回类型非常重要;也就是说,当表达式的数据类型类似于以下内容时,数学运算符的工作效果会更好 IntegerNumeric ,JSON访问器为了正常工作需要使用如下类型 JSON 。某些函数类返回整行而不是列值,在这种情况下需要引用特定的列;此类函数称为 table valued functions

对于SQLAlChemy必须应用结果集处理的情况,在执行语句和取回行时,函数的SQL返回类型可能也很重要。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 DataType中,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 ::

>>> 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端进行任何特殊类型处理,而且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()

    对比::

    >>> func.json_object('{"a", "b"}').type
    NullType()
  2. 需要函数感知表达式支持;这通常是指与数据类型相关的特殊运算符,例如 JSONARRAY

  3. 需要对结果值进行处理,这可能包括以下类型 DateTimeBooleanEnum ,或者同样是特殊的数据类型,如 JSONARRAY

使用窗口函数

窗口函数是SQL聚合函数的特殊用法,它在处理单个结果行时计算组中返回的行的聚合值。而像这样的函数 MAX() 将为您提供一组行中某列的最高值,使用与“窗口函数”相同的函数将为您提供每行的最高值, 从那一行开始

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

在SQLAlChemy中,由 func 命名空间包括一个方法 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条款内呈现。我们也可以利用 ORDER BY 子句使用 FunctionElement.over.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聚合函数,则数据库将拒绝该表达式。

集团内特殊修改器,过滤

“组内”SQL语法与“有序集”或“假设集”聚合函数一起使用。常见的“有序集”函数包括 percentile_cont()rank() 。SQLAlChemy包括内置实现 rankdense_rankmodepercentile_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))

某些后端支持“过滤”,以将聚合函数的范围限制为与返回的总行范围相比的特定行子集,可使用 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和面向数组的函数,以及 generate_series() ,表值函数在FROM子句中指定,然后作为表引用,有时甚至作为列引用。这种形式的函数在PostgreSQL数据库中非常突出,但是SQLite、Oracle和SQL Server也支持某些形式的表值函数。

参见

表值、表值函数和列值函数、行和元组对象 -在 《PostgreSQL》 文档。

虽然许多数据库支持表值和其他特殊形式,但PostgreSQL往往是对这些功能需求最大的地方。有关PostgreSQL语法的其他示例以及其他功能,请参阅本节。

SQLAlChemy提供了 FunctionElement.table_valued() 方法作为基本的“表值函数”构造,它将把一个 func 对象添加到包含一系列命名列的FROM子句中,该方法基于按位置传递的字符串名称。这将返回一个 TableValuedAlias 对象,该对象是启用功能的 Alias 可以用作任何其他FROM子句的构造,如 使用别名 。下面我们将说明 json_each() 函数,虽然该函数在PostgreSQL上很常见,但SQLite的现代版本也支持该函数:

>>> 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)
...     print(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

在上面,我们使用了 json_each() SQLite和PostgreSQL支持的JSON函数,用于生成表值表达式,其中单个列引用为 value ,然后选择其三行中的两行。

参见

表值函数 -在 《PostgreSQL》 文档-本节将详细介绍其他语法,如已知可与PostgreSQL一起使用的特殊列派生和“具有序号”。

列值函数-作为标量列的表值函数

PostgreSQL和Oracle支持的一种特殊语法是引用FROM子句中的函数,然后该函数作为SELECT语句或其他列表达式上下文的COLUMNS子句中的单个列交付。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方言也支持“Column Valued”表单,它可用于自定义SQL函数:

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

参见

列值函数 -在 《PostgreSQL》 文档。

SQLAlchemy 1.4 / 2.0 Tutorial

下一教程部分: 使用核心更新和删除行