如何在 SqlAlchemy 上订购嵌套的 SQL SELECT

Posted

技术标签:

【中文标题】如何在 SqlAlchemy 上订购嵌套的 SQL SELECT【英文标题】:How to order nested SQL SELECT on SqlAlchemy 【发布时间】:2020-11-28 18:13:22 【问题描述】:

我编写了一个 SQL 脚本,它应该在用户 id 之前获取 X 个条目,根据注册日期在有序 db 表上按registration_date desc 排序。

更具体地说,假设这些是有序数据库中的一些条目:

    id      | Name                  | Email                           | registration_data
    3939    | Barbara Hayes         | barbara.hayes@example.com       | 2019-09-15T23:39:26.910Z
    689     | Noémie Harris         | noemie.harris@example.com       | 2019-09-14T21:39:15.641Z
    2529    | Andrea Iglesias       | andrea.iglesias@example.com     | 2019-09-13T02:59:08.821Z
    3890    | Villads Andersen      | villads.andersen@example.com    | 2019-09-12T06:29:48.708Z
    3685    | Houssine Van Sabben   | houssine.vansabben@example.com  | 2019-09-12T02:27:08.396Z

我想让用户超过 id 3890。所以查询应该返回

689     | Noémie Harris         | noemie.harris@example.com       | 2019-09-14T21:39:15.641Z
2529    | Andrea Iglesias       | andrea.iglesias@example.com     | 2019-09-13T02:59:08.821Z

我写的原始 SQL 是这样的:

SELECT * from (
        SELECT id, name, email, registration_date FROM public.users
        WHERE users.registration_date > (SELECT registration_date FROM users WHERE id = 3890)
        order by registration_date
        limit 2 )
    as a
order by registration_date desc

见this dbfiddle。

我尝试实现 SqlAlchemy 代码,但没有成功。我相信我在子查询上犯了一个错误。这是我到目前为止所做的。

registration_date_min = db.query(User.registration_date) \
    .order_by(User.registration_date) \
    .filter(User.id == ending_before).first()

users_list = db.query(User) \
    .filter(User.registration_date > registration_date_min) \
    .order_by('registration_date').limit(limit).subquery('users_list')

return users_list.order_by(desc('registration_date'))

P.s ending_before 代表一个 user_id。就像示例中的 3890 一样。 关于 SqlAlchemy 部分的任何想法都会非常有帮助!

【问题讨论】:

codeshare.io/5QwQEm 真的快吗? @MartijnPieters 表格的示例只是按日期降序排列,这就是具体日期的原因。说实话,我对你所说的示例日期有点困惑,这就是我发布代码共享的原因,以便在那里更快地讨论它。从好的方面来说,我尝试了你的解决方案,它奏效了! 现在在移动设备上。我把查询放到一个 dbfiddle 上,发现我一定把用户 ID 弄混了。 :-/ 很抱歉! 【参考方案1】:

首先,你的registration_date_min查询已经被执行了;你在那里有一列有一列。删除first() 调用;它执行SELECT 并返回第一行。

当您通过主键进行选择时,只会有一行,您不需要对其进行排序。只需使用:

registration_date_min = db.query(User.registration_date).filter(
    User.id == ending_before
)

现在是一个查询对象,可以直接用于比较:

users_list = (
    db.query(User)
    .filter(User.registration_date > registration_date_min)
    .order_by(User.registration_date)
    .limit(limit)
)

然后您可以从该查询中self-select with Query.from_self() 应用最终排序:

return user_list.from_self().order_by(User.registration_date.desc()))

这会产生以下 SQL(在 SQLite 上,其他方言可能不同):

SELECT anon_1.users_id AS anon_1_users_id, anon_1.users_name AS anon_1_users_name, anon_1.users_email AS anon_1_users_email, anon_1.users_registration_date AS anon_1_users_registration_date 
FROM (SELECT users.id AS users_id, users.name AS users_name, users.email AS users_email, users.registration_date AS users_registration_date 
FROM users 
WHERE users.registration_date > (SELECT users.registration_date AS users_registration_date 
FROM users 
WHERE users.id = ?) ORDER BY users.registration_date
 LIMIT ? OFFSET ?) AS anon_1 ORDER BY anon_1.users_registration_date DESC

如果我将以下模型与__repr__ 一起使用:

class User(db.Model):
    __tablename__ = "users"

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String)
    email = db.Column(db.String)
    registration_date = db.Column(db.DateTime)

    def __repr__(self):
        return f"<User(self.id, self.name!r, self.email!r, self.registration_date!r>"

并打印我得到的查询结果实例:

<User(689, 'Noémie Harris', 'noemie.harris@example.com', datetime.datetime(2019, 9, 14, 21, 39, 15, 641000)>
<User(2529, 'Andrea Iglesias', 'andrea.iglesias@example.com', datetime.datetime(2019, 9, 13, 2, 59, 8, 821000)>

【讨论】:

以上是关于如何在 SqlAlchemy 上订购嵌套的 SQL SELECT的主要内容,如果未能解决你的问题,请参考以下文章

Rails:在嵌套视图中订购商品

如何以灵活的方式将嵌套的 pydantic 模型用于 sqlalchemy

嵌套的一对多关系 sqlalchemy 过滤

使用 Prisma 订购嵌套/二级数组?

如何使用 Python 在 AWS Lambda 上运行 SQLAlchemy

Django模板:如何通过每个字典的嵌套属性重新组合字典列表?