#sql #postgresql #subquery #sql-order-by #greatest-n-per-group
#sql #postgresql #подзапрос #sql-порядок-по #наибольшее число пользователей на группу
Вопрос:
Я написал запрос для получения таких данных (прикрепленное изображение), user_id и topic_id группируются и вычисляется среднее значение оценки. Я хочу ранжировать оценку для каждого пользователя, чтобы каждый пользователь имел ранг 1 для своего самого высокого topic_id. Как мы можем написать запрос для ранжирования их результатов, потому что мне нужно выбрать верхнюю строку для каждого пользователя.
Таблица выглядит следующим образом —
USER_ID TOPIC_ID SCORE
------------- ------------- ------
b33e3100a7be 829e4b89c318 85
b33e3100a7be b19b6f2b2975 82
b33e3100a7be e305c970701c 81.6
b33e3100a7be 6c6fac161e65 81.6
7379ce6bc5a9 6c6fac161e65 54.6
7379ce6bc5a9 e305c970701c 54.6
7379ce6bc5a9 b19b6f2b2975 51.6
и хочу, чтобы результат выглядел примерно так.
USER_ID TOPIC_ID SCORE RANK
------------- ------------- ------ -----
b33e3100a7be 829e4b89c318 85 1
b33e3100a7be b19b6f2b2975 82 2
b33e3100a7be e305c970701c 81.6 3
b33e3100a7be 6c6fac161e65 81.6 3
7379ce6bc5a9 6c6fac161e65 54.6 1
7379ce6bc5a9 e305c970701c 54.6 1
7379ce6bc5a9 b19b6f2b2975 54.6 2
Ответ №1:
Вы можете использовать оконные функции, чтобы получить все лучшие оценки:
select t.*
from (select t.*,
rank() over (partition by user_id order by score desc) as seqnum
from t
) t
where seqnum = 1;
В этом контексте rank()
возвращаются все самые высокие темы, если есть ничья. row_number()
возвращает один произвольно.
Вы также можете использовать коррелированный подзапрос:
select t.*
from t
where t.score = (select max(t2.score)
from t t2
where t2.user_id = t.user_id
);
Обратите внимание, что оба этих метода могут быть включены в запрос агрегирования. Нет необходимости сохранять промежуточные результаты, чтобы получить то, что вы хотите.
Ответ №2:
Вы можете получить строку с наивысшим баллом за каждое использование с помощью distinct on
:
select distinct on (user_id) t.*
from mytable t
order by user_id, score desc
Вероятно, мы могли бы дополнительно оптимизировать код, если бы вы показывали данные, с которых вы начинаете, и запрос, который вы использовали для создания промежуточного результирующего набора, который вы показываете. Возможно, вы захотите задать новый вопрос для этого, предоставив надлежащие образцы данных, желаемые результаты и существующий код.
Если вы хотите разрешить привязки, то вы можете использовать rank()
:
select *
from (
select t.*,
rank() over(partition by user_id order by score desc) rn
from mytable t
) t
where rn = 1
Или, в Postgres 13:
select t.*
from mytable t
order by rank() over(partition by user_id order by score desc)
fetch first 1 row with ties
Комментарии:
1. Я отредактировал вопрос, пример таблицы и добавил к нему выходные данные.
2. Но если у пользователя есть 2 разные темы, и обе имеют одинаковую оценку, и они являются двумя верхними строками для этого пользователя, мне нужны обе строки, а не одна.