问题描述
以下内容是有关使用python编写的Web应用程序后端,并使用sqlAlchemy连接到Postgres(v9.5)关系数据库。
假设我们有两个表:项和关系。每个项目都有一个ID,并且可能与多个其他项目相关。此类关系会保留在关系表中,该表存储自己的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代码提取,以返回所有项以及附加的派生-或伪列包含每个元组的关系数据作为结构数组:
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文档和许多网站之后,我在这里找到了基于相关子查询的解决方案:
Can we make correlated queries with SQLAlchemy-答案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('item1_id',Column('item2_id',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)
)