#sql #average #aggregate-functions #unpivot
#sql #среднее #агрегатные функции #отменить
Вопрос:
У меня есть следующая таблица
User A | User B | Value
------- -------- ------
1 | 2 | 60
3 | 1 | 10
4 | 5 | 50
3 | 5 | 50
5 | 1 | 80
2 | 3 | 10
Я хочу сгруппировать записи, в которых либо пользователь a = x, либо пользователь b = x, чтобы найти средние значения.
например, пользователь 1 появляется в таблице 3 раза, один раз как «Пользователь A» и дважды как «Пользователь B». Поэтому я хотел бы выполнить свою AVG()
функцию, используя эти три строки.
Мне нужны самые высокие и самые низкие средние значения. Такой запрос разбил бы приведенную выше таблицу на следующие группы:
User | Avg Value
----- -----
1 | 50
2 | 35
3 | 23.33
4 | 50
5 | 60
а затем возвращает
Highest Avg | Lowest Avg
------------ -----------
60 | 23.33
Я знаю, что GROUP BY
собирает вместе записи, в которых столбец имеет одинаковое значение. Я хочу собрать вместе записи, в которых любой из двух столбцов имеет одинаковое значение. Я искал много решений, но, похоже, не могу найти того, которое соответствует моей проблеме.
Комментарии:
1. Пометьте свой вопрос базой данных, которую вы используете.
Ответ №1:
Переносимый параметр использует union all
:
select usr, avg(value) avg_value
from (
select usera usr, value from mytable
union all select userb, value from mytable
) t
group by usr
Это дает вам первый результирующий набор. Затем вы можете добавить еще один уровень агрегирования, чтобы получить максимальное и минимальное среднее значение:
select min(avg_value) min_avg_value, max(avg_value) max_avg_value
from (
select usr, avg(value) avg_value
from (
select usera usr, value from mytable
union all select userb, value from mytable
) t
group by usr
) t
В базах данных, которые поддерживают боковые соединения и values()
, это наиболее удобно (и эффективно) выражается следующим образом:
select min(avg_value) min_avg_value, max(avg_value) max_avg_value
from (
select usr, avg(value) avg_value
from mytable t
cross join lateral (values (usera, value), (userb, value)) as x(usr, value)
group by usr
) t
Это будет работать, например, в Postgres. В SQL Server вы бы просто заменили cross join lateral
на cross apply
.
Комментарии:
1. Фантастика, я ценю, что вы разбили его, чтобы показать, как получить первый результирующий набор — эта часть будет полезна для ситуаций, отличных от той, о которой я задал вопрос.
2. Должен ли быть usr после userb?
Ответ №2:
Вы можете отменить использование union all
, а затем агрегировать:
select user, avg(value)
from ((select usera as user, value) union all
(select userb as user, value)
) u
group by user;
Вы можете получить экстремумы с другим уровнем агрегирования:
select min(avg_value), max(avg_value)
from (select user, avg(value) as avg_value
from ((select usera as user, value) union all
(select userb as user, value)
) u
group by user
) ua