SQLAlchemy - подзапрос в предложении WHERE - PullRequest
47 голосов
/ 01 июня 2011

Я только недавно начал использовать SQLAlchemy и до сих пор не могу обернуть голову вокруг некоторых концепций.

Сводим к основным элементам, у меня есть две таблицы, подобные этой (это с помощью Flask-SQLAlchemy):

class User(db.Model):
    __tablename__ = 'users'
    user_id = db.Column(db.Integer, primary_key=True)

class Posts(db.Model):
    __tablename__ = 'posts'
    post_id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('users.user_id'))
    post_time = db.Column(db.DateTime)

    user = db.relationship('User', backref='posts')

Как мне поступить с запросом списка пользователей и их последних сообщений (исключая пользователей без сообщений). Если бы я использовал SQL, я бы сделал:

SELECT [whatever]
FROM posts AS p
    LEFT JOIN users AS u ON u.user_id = p.user_id
WHERE p.post_time = (SELECT MAX(post_time) FROM posts WHERE user_id = u.user_id)

Таким образом, я точно знаю «желаемый» SQL, чтобы получить желаемый эффект, но не знаю, как «правильно» выразить его в SQLAlchemy.

Редактировать: если это важно, я использую SQLAlchemy 0.6.6.

Ответы [ 2 ]

60 голосов
/ 13 июня 2011

предыдущий ответ работает, но точный sql, который вы запрашивали, написан во многом как фактическое утверждение:

print s.query(User, Posts).\
    outerjoin(Posts.user).\
    filter(Posts.post_time==\
        s.query(
            func.max(Posts.post_time)
        ).
        filter(Posts.user_id==User.user_id).
        correlate(User).
        as_scalar()
    )

Я думаю, что "концепция", которая не обязательно очевидна, заключается в том, что as_scalar ()в настоящее время требуется для создания подзапроса в качестве «скаляра» (вероятно, следует предположить, что из контекста против ==).

Редактировать: Подтверждено, что это ошибочное поведение, выполненный тикет # 2190.В текущем выпуске или выпуске 0.7.2 as_scalar () вызывается автоматически, и вышеупомянутый запрос может быть:

print s.query(User, Posts).\
    outerjoin(Posts.user).\
    filter(Posts.post_time==\
        s.query(
            func.max(Posts.post_time)
        ).
        filter(Posts.user_id==User.user_id).
        correlate(User)
    )
51 голосов
/ 02 июня 2011

Это должно работать (другой SQL, тот же результат):

t = Session.query(
    Posts.user_id,
    func.max(Posts.post_time).label('max_post_time'),
).group_by(Posts.user_id).subquery('t')

query = Session.query(User, Posts).filter(and_(
    User.user_id == Posts.user_id,
    User.user_id == t.c.user_id,
    Posts.post_time == t.c.max_post_time,
))

for user, post in query:
    print user.user_id, post.post_id

Где c обозначает «столбцы»

...