SQLAlchemy 查询将相关数据的伪列添加为字典数组
SQLAlchemy query adding pseudo-column of related data as an array of dicts
以下内容与用 python 编写并使用 SQLAlchemy 连接到 postgres (v9.5) 关系数据库的 Web 应用程序后端有关。
假设我们有两个 table:items 和 relations。每个项目都有一个 ID,并且可能与多个其他项目相关。这样的关系保存在关系 table 中,它存储自己的 ID、两个相关项目的 ID 加上关系的名称。
这是一些示例数据:
items: relations:
id name id item1_id item2_id name
--------------- ----------------------------------
1 car 1 1 2 weight
2 boat 2 1 3 colour
3 bike 3 1 4 age
4 jet-pack 4 5 1 age
5 plane
6 rocket
因此,例如 car 和 bike 通过颜色相关。
我正在寻找一个 SQLAlchemy 代码摘录,用于对 return 所有 项目 的数据库查询以及一个额外的 derived- 或 伪列 包含每个元组的关系数据作为结构数组:
id name relations
---------------------------------------------------------------------------------
1 car [ { 2, weight }, { 3, colour }, { 4, age }, { 5, age } ]
2 boat [ { 1, weight } ]
3 bike [ { 1, colour } ]
4 jet-pack [ { 1, age } ]
5 plane [ { 1, age } ]
6 rocket []
一个好的第一步是在尝试转换为 SQLAlchemy 之前生成 SQL 查询。我取得的最接近上述结果:
SELECT i.*,
(SELECT array_agg(row(CASE WHEN r.item1_id=i.id THEN r.item2_id ELSE r.item1_id END, i.name))
AS relations from relations r
WHERE r.item1_id=i.id OR r.item2_id=i.id)
FROM items i ;
但更好一点的是实际输出关系 json:
SELECT i.*,
(SELECT jsonb_agg(jsonb_build_object('item', CASE WHEN r.item1_id=i.id THEN r.item2_id ELSE r.item1_id END, 'relation', r.name))
AS relations
FROM relations r
WHERE r.item1_id=i.id OR r.item2_id=i.id)
FROM items i;
产生:
id name relations
---------------------------------------------------------------------------------
1 car [ { "item": 2, "relation": "weight" }, { "item": 3, "relation": "colour" }, { "item": 4, "relation": "age" }, { "item": 5, "relation": "age" } ]
2 boat [ { "item": 1, "relation": "weight" } ]
3 bike [ { "item": 1, "relation": "colour" } ]
4 jet-pack [ { "item": 1, "relation": "age" } ]
5 plane [ { "item": 1, "relation": "age" } ]
6 rocket []
这个子查询在sqlalchemy中是怎么实现的?
经过长时间浏览 SQLalchemy 文档和许多 web-sites,我在此处找到了基于相关 sub-query 的解决方案:
- 答案 #1。
按如下方式调整它会产生我要找的东西:
from sqlalchemy import select, func, table, Column, Integer, Text, case
from sqlalchemy.sql.expression import or_
items = table('items', Column('id', Integer),
Column('name', Text))
relations = table('relations', Column('id', Integer),
Column('item1_id', Integer),
Column('item2_id', Integer),
Column('name', Text))
subquery = select( [ func.jsonb_agg(
func.jsonb_build_object(
'item', case( [ ( relations.c.item1_id == items.c.id, relations.c.item2_id ) ],
else_ = relations.c.item1_id),
'relation', relations.c.name
)
)
]
).where(or_(relations.c.item1_id == items.c.id, relations.c.item2_id == items.c.id) \
.correlate(items)
query = (
select([items.*, subquery.label('relations')]).select_from(items)
)
以下内容与用 python 编写并使用 SQLAlchemy 连接到 postgres (v9.5) 关系数据库的 Web 应用程序后端有关。
假设我们有两个 table:items 和 relations。每个项目都有一个 ID,并且可能与多个其他项目相关。这样的关系保存在关系 table 中,它存储自己的 ID、两个相关项目的 ID 加上关系的名称。
这是一些示例数据:
items: relations:
id name id item1_id item2_id name
--------------- ----------------------------------
1 car 1 1 2 weight
2 boat 2 1 3 colour
3 bike 3 1 4 age
4 jet-pack 4 5 1 age
5 plane
6 rocket
因此,例如 car 和 bike 通过颜色相关。
我正在寻找一个 SQLAlchemy 代码摘录,用于对 return 所有 项目 的数据库查询以及一个额外的 derived- 或 伪列 包含每个元组的关系数据作为结构数组:
id name relations
---------------------------------------------------------------------------------
1 car [ { 2, weight }, { 3, colour }, { 4, age }, { 5, age } ]
2 boat [ { 1, weight } ]
3 bike [ { 1, colour } ]
4 jet-pack [ { 1, age } ]
5 plane [ { 1, age } ]
6 rocket []
一个好的第一步是在尝试转换为 SQLAlchemy 之前生成 SQL 查询。我取得的最接近上述结果:
SELECT i.*,
(SELECT array_agg(row(CASE WHEN r.item1_id=i.id THEN r.item2_id ELSE r.item1_id END, i.name))
AS relations from relations r
WHERE r.item1_id=i.id OR r.item2_id=i.id)
FROM items i ;
但更好一点的是实际输出关系 json:
SELECT i.*,
(SELECT jsonb_agg(jsonb_build_object('item', CASE WHEN r.item1_id=i.id THEN r.item2_id ELSE r.item1_id END, 'relation', r.name))
AS relations
FROM relations r
WHERE r.item1_id=i.id OR r.item2_id=i.id)
FROM items i;
产生:
id name relations
---------------------------------------------------------------------------------
1 car [ { "item": 2, "relation": "weight" }, { "item": 3, "relation": "colour" }, { "item": 4, "relation": "age" }, { "item": 5, "relation": "age" } ]
2 boat [ { "item": 1, "relation": "weight" } ]
3 bike [ { "item": 1, "relation": "colour" } ]
4 jet-pack [ { "item": 1, "relation": "age" } ]
5 plane [ { "item": 1, "relation": "age" } ]
6 rocket []
这个子查询在sqlalchemy中是怎么实现的?
经过长时间浏览 SQLalchemy 文档和许多 web-sites,我在此处找到了基于相关 sub-query 的解决方案:
按如下方式调整它会产生我要找的东西:
from sqlalchemy import select, func, table, Column, Integer, Text, case
from sqlalchemy.sql.expression import or_
items = table('items', Column('id', Integer),
Column('name', Text))
relations = table('relations', Column('id', Integer),
Column('item1_id', Integer),
Column('item2_id', Integer),
Column('name', Text))
subquery = select( [ func.jsonb_agg(
func.jsonb_build_object(
'item', case( [ ( relations.c.item1_id == items.c.id, relations.c.item2_id ) ],
else_ = relations.c.item1_id),
'relation', relations.c.name
)
)
]
).where(or_(relations.c.item1_id == items.c.id, relations.c.item2_id == items.c.id) \
.correlate(items)
query = (
select([items.*, subquery.label('relations')]).select_from(items)
)