是否有约定/是否可以返回列中嵌套对象的数组/列表,例如来自连接的多对多表?
例如,假设我有三个标准化表:“movies”、“actors”和一个联结表“actors_in_movies”。我想检索多部电影,每行一部电影,并在其行中包含该电影的演员列表。
连接表上的基本 SELECT 语句将检索:
《比尔和泰德的奇妙冒险》、《基努·里维斯》
《比尔和泰德的奇妙冒险》、《亚历克斯·温特》
《比尔和泰德的奇妙冒险》、《乔治·卡林》
《黑客帝国》、《基努·里维斯》
《黑客帝国》、《凯莉-安·莫斯》
《黑客帝国》、《劳伦斯·菲什伯恩》
使用 func.group_concat() 我可以检索:
“比尔和泰德的奇妙冒险”、“基努·里维斯、亚历克斯·温特、乔治·卡林”
《黑客帝国》、《基努·里维斯、凯莉-安·莫斯、劳伦斯·菲什伯恩》
使用 GROUP_BY 和像 COUNT 这样的聚合函数我可以检索:
“比尔和泰德的奇妙冒险”,3
《黑客帝国》,3
但是 SQL(以及扩展的 SQL Alchemy Core)是否支持返回一些我可以访问嵌套聚合结果的内容,例如:
“比尔和泰德的奇妙冒险”、[“基努·里维斯”、“亚历克斯·温特”、“乔治·卡林”]
《黑客帝国》、[《基努·里维斯》、《凯莉·安·莫斯》、《劳伦斯·菲什伯恩》]
我知道这是 SQLAlchemy ORM 的主要用例 - 是否有一种传统方法可以使用纯 SQL/SQL Alchemy Core 来管理它?是否需要多次查询?还是通常留给应用层来管理?
有没有一种传统的方法来使用纯 SQL/SQL Alchemy Core 来管理这个问题?
简而言之:不,Core 中没有这种独立于数据库的机制。这就是 ORM 的用途。
但是,Core 可以在特定数据库中利用这样的功能。例如,PostgreSQL 有
array_agg()
:
from sqlalchemy import Column, ForeignKey, Integer, MetaData, String, Table, create_engine, func, insert, select
engine = create_engine("postgresql://scott:[email protected]/test")
meta = MetaData()
movie = Table(
"movie",
meta,
Column("id", Integer, primary_key=True, autoincrement=False),
Column("title", String, nullable=False),
)
actor = Table(
"actor",
meta,
Column("id", Integer, primary_key=True, autoincrement=False),
Column("name", String, nullable=False),
)
actor_in_movie = Table(
"actor_in_movie",
meta,
Column("actor_id", Integer, ForeignKey("actor.id"), primary_key=True),
Column("movie_id", Integer, ForeignKey("movie.id"), primary_key=True),
)
meta.drop_all(engine)
meta.create_all(engine)
# populate example data
with engine.begin() as conn:
conn.execute(
insert(movie),
[
dict(id=1, title="Bill & Ted's Excellent Adventure"),
],
)
conn.execute(
insert(actor),
[
dict(id=1, name="Reeves, Keanu"),
dict(id=2, name="Carlin, George"),
],
)
conn.execute(
insert(actor_in_movie),
[
dict(actor_id=1, movie_id=1),
dict(actor_id=2, movie_id=1),
],
)
with engine.begin() as conn:
qry = (
select(movie.c.title, func.array_agg(actor.c.name).label("actors"))
.select_from(actor)
.join(actor_in_movie)
.join(movie)
.group_by(movie.c.title)
)
result = conn.execute(qry)
for row in result:
print(row)
# ("Bill & Ted's Excellent Adventure", ['Reeves, Keanu', 'Carlin, George'])
print(type(row.actors)) # <class 'list'>
print(type(row.actors[0])) # <class 'str'>