我有以下SQLAlchemy表:
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class NetworkLink(Base):
"""Network immediate link between a franchisee and his franchisor
"""
__tablename__ = 'network_link'
id_franchisee = Column(Integer, ForeignKey('user.id'), primary_key=True)
id_franchisor = Column(Integer, ForeignKey('user.id'))
基本上代表了树状网络结构。
鉴于特许人的ID,我需要获取整个子树中所有后代的ID。 例如,表如下:
id_franchisor | id_franchisee
1 | 2
1 | 3
2 | 4
2 | 5
4 | 6
然后给定id 1,我需要1,2,3,4,5,6,而给定id 2,我需要2,4,5,6。
我知道这不是解决此问题的最有效的表表示形式,但是该操作很少执行,并且插入操作更为常见。
我正在尝试通过递归查询来实现这一点,该查询看起来像这样:
"""
WITH RECURSIVE recursive_franchisee(id) AS
(
SELECT %s
UNION ALL
SELECT L.id_franchisee
FROM recursive_franchisee as R JOIN network_link as L ON R.id = L.id_franchisor
) SELECT id FROM recursive_franchisee;
"""
我可以在其中用%s
代替我想要的ID。我已经对其进行了测试,并且效果很好。
但是,我想避免使用硬编码的原始查询,因此我试图在SQLAlchemy中重新创建它,但是失败了。
查看中的文档
https://docs.sqlalchemy.org/en/latest/orm/query.html#sqlalchemy.orm.query.Query.cte
我想出了两个替代方案,它们足够接近但不能完全正常工作。
第一个几乎是正确的,但我不知道如何指定起始ID:
rec = db_session.query("id").cte(recursive=True, name="recursive_franchisee")
ralias = sqlalchemy.orm.aliased(rec, name="R")
lalias = sqlalchemy.orm.aliased(NetworkLink, name="L")
rec = rec.union_all(
db_session.query(lalias.id_franchisee) \
.join(ralias, ralias.c.id == lalias.id_franchisor)
)
qr = db_session.query(rec)
sqr = str(qr)
# sqr equals to:
"""
WITH RECURSIVE recursive_franchisee(id) AS
(
SELECT id # how do I specify an id here?
UNION ALL
SELECT "L".id_franchisee AS "L_id_franchisee"
FROM network_link AS "L" JOIN recursive_franchisee AS "R" ON "R".id = "L".id_franchisor
)
SELECT recursive_franchisee.id FROM recursive_franchisee
"""
我还尝试了以下操作,但有一个不同的问题:
rec = db_session.query(NetworkLink.id_franchisor) \
.filter(NetworkLink.id_franchisor == 1) \
.cte(recursive=True, name="recursive_franchisee")
ralias = sqlalchemy.orm.aliased(rec, name="R")
lalias = sqlalchemy.orm.aliased(NetworkLink, name="L")
rec = rec.union_all(
db_session.query(lalias.id_franchisee) \
.join(ralias, ralias.c.id_franchisor == lalias.id_franchisor)
)
qr = db_session.query(rec)
sqr = str(qr)
# sqr equals to:
"""
WITH RECURSIVE recursive_franchisee(id_franchisor) AS
(
SELECT network_link.id_franchisor AS id_franchisor
FROM network_link
WHERE network_link.id_franchisor = ?
UNION ALL
SELECT "L".id_franchisee AS "L_id_franchisee"
FROM network_link AS "L" JOIN recursive_franchisee AS "R" ON "R".id_franchisor = "L".id_franchisor
)
SELECT recursive_franchisee.id_franchisor AS recursive_franchisee_id_franchisor
FROM recursive_franchisee
"""
这当然会产生重复的结果,因为前一个SELECT
会多次返回特许人(每个特许人一个)。我可以使用DISTINCT
,但我想避免选择一开始就已经知道的东西。
如何在SQLAlchemy中实现所需的查询?
编辑:
在IljaEverilä的评论之后,我提出了以下可行的解决方案:
from sqlalchemy.sql.expression import literal
rec = db_session.query(literal(current_user.id).label("id")) \
.cte(recursive=True, name="recursive_franchisee")
ralias = sqlalchemy.orm.aliased(rec, name="R")
lalias = sqlalchemy.orm.aliased(NetworkLink, name="L")
rec = rec.union_all(
db_session.query(lalias.id_franchisee) \
.join(ralias, ralias.c.id == lalias.id_franchisor)
)
qr = db_session.query(rec)
谢谢,如果您发布答案,我会接受的。