Как рассчитать среднее значение с помощью sqlalchemy? - PullRequest
0 голосов
/ 27 мая 2020

Как мне получить среднее количество в SQLAlchemy? Я много чего пробовал, но все не получилось. Код:

month = sqlalchemy.func.date_trunc('month', Complaint.date)
records_count = sqlalchemy.sql.func.count(ClientGroupRecord.id)
complained_clients = session.query(Friend, records_count.label('count'), month). \
    filter(Friend.friend_id == friend_id). \
    join(Complaint, Complaint.friend == Friend.friend_id). \
    join(ClientGroup, Complaint.client_group == ClientGroup.client_group_id). \
    join(ClientGroupRecord, ClientGroup.client_group_id == ClientGroupRecord.client_group_id). \
    join(Client, Client.client_id == ClientGroupRecord.client_id). \
    group_by(month, Friend.friend_id)

records_avg = sqlalchemy.sql.func.avg(records_count)

result = ???

В result мне не нужен Friend, но мне нужно avg в столбце, помеченном count, сгруппированным по month столбцу.

1 Ответ

1 голос
/ 27 мая 2020

Один из вариантов - использовать оконные функции, если ваша СУБД их поддерживает - на основе использования date_trunc, я полагаю, вы используете PostgreSQL. Причина, по которой это работает, заключается в том, что оконные функции оцениваются после того, как GROUP BY создала групповые строки. Так что оберните счетчик в AVG(...) OVER (), где окно представляет собой весь набор результатов:

from sqlalchemy import func

month = func.date_trunc('month', Complaint.date)
records_count = func.count(ClientGroupRecord.id)
# Create a window of the whole set of results
records_avg = func.avg(records_count).over()

complained_clients = session.query(records_count.label('count'),
                                   records_avg.label('avg'),
                                   month.label('month')). \
    select_from(Friend). \
    join(Complaint, Complaint.friend == Friend.friend_id). \
    join(ClientGroup, Complaint.client_group == ClientGroup.client_group_id). \
    join(ClientGroupRecord, ClientGroup.client_group_id == ClientGroupRecord.client_group_id). \
    join(Client, Client.client_id == ClientGroupRecord.client_id). \
    filter(Friend.friend_id == friend_id). \
    group_by(month). \
    all()

Обратите внимание, что Friend перемещен из query(...), чтобы явно использоваться как левая часть первое соединение с Query.select_from(). Это устраняет необходимость использовать Friend.id в предложении GROUP BY, даже если вы выбираете только друзей с определенным идентификатором.

Если вас вообще не интересуют ежемесячные подсчеты, а нужно только среднее значение, более традиционный подход подзапрос так же хорош:

from sqlalchemy import func

month = func.date_trunc('month', Complaint.date)
records_count = func.count(ClientGroupRecord.id)

complained_clients = session.query(records_count.label('count')). \
    select_from(Friend). \
    join(Complaint, Complaint.friend == Friend.friend_id). \
    join(ClientGroup, Complaint.client_group == ClientGroup.client_group_id). \
    join(ClientGroupRecord, ClientGroup.client_group_id == ClientGroupRecord.client_group_id). \
    join(Client, Client.client_id == ClientGroupRecord.client_id). \
    filter(Friend.friend_id == friend_id). \
    group_by(month). \
    subquery()

result = session.query(func.avg(complained_clients.c.count)).scalar()

... или использование оконного подхода:

from sqlalchemy import func

month = func.date_trunc('month', Complaint.date)
records_count = func.count(ClientGroupRecord.id)
# Create a window of the whole set of results
records_avg = func.avg(records_count).over()

# Windows are evaluated for each row, but here we have a single window spanning
# the entire result, so the use of DISTINCT collapses this to a single value.
# Knowing what the query does LIMIT 1 / FETCH FIRST 1 ROW ONLY would work as well.
result = session.query(records_avg.label('avg')). \
    select_from(Friend). \
    join(Complaint, Complaint.friend == Friend.friend_id). \
    join(ClientGroup, Complaint.client_group == ClientGroup.client_group_id). \
    join(ClientGroupRecord, ClientGroup.client_group_id == ClientGroupRecord.client_group_id). \
    join(Client, Client.client_id == ClientGroupRecord.client_id). \
    filter(Friend.friend_id == friend_id). \
    group_by(month). \
    distinct(). \
    scalar()
...