SQLAlchemy 递归查询结果集排序
Posted
技术标签:
【中文标题】SQLAlchemy 递归查询结果集排序【英文标题】:SQLAlchemy recursive query result set ordering 【发布时间】:2021-12-24 07:27:07 【问题描述】:我正在尝试使用 Python (3.9.6) 和 Flask-SqlAlchemy (2.5) 的递归查询来获取分层数据。这使用自引用数据,其中记录的 parent_id 指向同一表中的另一条记录。这是我的模型的简化版本:
class Post(db.Model):
__tablename__ = "post"
post_id = db.Column(db.String, primary_key=True)
parent_id = db.Column(db.String, db.ForeignKey("post.post_id"), default=None)
content = db.Column(db.String)
children = db.relationship("Post", backref=db.backref("parent", remote_side=[post_id], lazy="joined"))
这是获取帖子及其所有子项的 SQLAlchemy 查询(它使用上下文管理器来确保在范围结束时提交或回滚数据库会话):
# post_id = unique id of a post in the database
with db_session_manager() as db_session:
# build the list of filters here to use in the CTE
filters = [
Post.post_id == post_id,
Post.parent_id == None
]
# construct a self-referential, hierarchal query for the
# post and it's comments
posts_hierarchy = (
db_session.query(Post, literal(0).label('level'))
.filter(*filters)
.cte(name='post_hierarchy', recursive=True)
)
parent = aliased(posts_hierarchy, name="p")
children = aliased(Post, name="c")
posts_hierarchy = (
posts_hierarchy.union_all(
db_session.query(Post, (parent.c.level + 1).label("level"))
.filter(children.parent_id == parent.c.post_id)
.filter(Post.post_id == children.post_id)
)
)
posts = (
db_session.query(Post, posts_hierarchy.c.level)
.select_entity_from(posts_hierarchy)
.all()
)
这在我取回根帖子及其所有子帖子时有效,但是,结果集包含以下内容:
root post
1st root child
2nd root child
3rd root child
1st child child (1)
2nd child child (2)
3rd child child (3)
2nd child child (4)
我想要得到的是这样的:
root post
1st root child
1st child child (1)
2nd child child (2)
3rd child child (3)
2nd root child
2nd child child (4)
3rd root child
etc
我的 SQL 不够好,无法获得我想要的结果。我可以尝试什么来解决这个问题?
【问题讨论】:
嗨,道格。如果可以,请不要在您的帖子上签名 - 从历史上看,用户卡已被认为足以用于此目的。 【参考方案1】:我已经弄清楚如何从递归 CTE 查询中获取我想要的内容。我这样修改了数据库表:
class Post(db.Model):
__tablename__ = "post"
post_id = db.Column(db.String, primary_key=True)
parent_id = db.Column(db.String, db.ForeignKey("post.post_id"), default=None)
sort_key = db.Column(db.String, nullable=False, default=SortKey())
content = db.Column(db.String)
children = db.relationship("Post", backref=db.backref("parent", remote_side=[post_id], lazy="joined"))
我添加了 sort_key 列,该列设置为自动递增函数,该函数返回值的字符串版本。我将递归 CTE SqlAlchemy 查询更新为:
# build the list of filters here to use in the CTE
filters = [
Post.post_uid == post_uid,
Post.parent_uid == None
]
if current_user.is_anonymous or current_user.can_view_posts():
filters.append(Post.active == True)
# build the recursive CTE query
hierarchy = (
db_session
.query(Post, Post.sort_key.label("sorting_key"))
.filter(*filters)
.cte(name='hierarchy', recursive=True)
)
children = aliased(Post, name="c")
hierarchy = hierarchy.union_all(
db_session
.query(
children,
(hierarchy.c.sorting_key + " " + children.sort_key).label("sorting_key")
)
.filter(children.parent_uid == hierarchy.c.post_uid)
)
# query the hierarchy for the post and it's comments
posts = (
db_session
.query(Post, hierarchy.c.sorting_key)
.select_entity_from(hierarchy)
.order_by(hierarchy.c.sorting_key)
.all()
)
合成的sorting_key
值是表中sort_key
的串联集,用于将结果正确排序为我正在寻找的分层格式。这基于两个资源 here 和 here。
希望其他人觉得这有帮助
【讨论】:
以上是关于SQLAlchemy 递归查询结果集排序的主要内容,如果未能解决你的问题,请参考以下文章