SQLAlchemy: как правильно использовать group_by () (only_full_group_by)? - PullRequest
0 голосов
/ 30 октября 2019

Я пытаюсь использовать group_by() функцию SQLAlchemy с механизмом mysql+mysqlconnector:

rows = session.query(MyModel) \
        .order_by(MyModel.published_date.desc()) \
        .group_by(MyModel.category_id) \
        .all()

Он отлично работает с SQLite, но для MySQL я получаю этоошибка:

[42000][1055] Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column '...' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

Я знаю , как решить ее простым SQL , но я бы хотел использовать преимущества SQLAlchemy.

Какое правильное решение сSQLAlchemy?

Заранее спасибо

1 Ответ

1 голос
/ 30 октября 2019

Один из способов сформировать запрос с четко определенным поведением состоит в использовании LEFT JOIN для поиска MyModel строк на category_id, которые не имеют подходящей строкис большим published_date:

my_model_alias = aliased(MyModel)

rows = session.query(MyModel).\
    outerjoin(my_model_alias,
              and_(my_model_alias.category_id == MyModel.category_id,
                   my_model_alias.published_date > MyModel.published_date)).\
    filter(my_model_alias.id == None).\
    all()

Это будет работать в любой СУБД SQL. В SQLite 3.25.0 и MySQL 8 (и многих других) вы можете использовать оконные функции для достижения того же:

sq = session.query(
        MyModel,
        func.row_number().
            over(partition_by=MyModel.category_id,
                 order_by=MyModel.published_date.desc()).label('rn')).\
    subquery()

my_model_alias = aliased(MyModel, sq)

rows = session.query(my_model_alias).\
    filter(sq.c.rn == 1).\
    all()

Конечно, вы также можете использовать GROUP BY, если вы затем используете результаты вобъединение:

max_pub_dates = session.query(
        MyModel.category_id,
        func.max(MyModel.published_date).label('published_date')).\
    group_by(MyModel.category_id).\
    subquery()

rows = session.query(MyModel).\
    join(max_pub_dates,
         and_(max_pub_dates.category_id == MyModel.category_id,
              max_pub_dates.published_date == MyModel.published_date)).\
    all()
...