Как усреднить / агрегировать результат SQL-запроса?

#sql #postgresql

#sql #postgresql

Вопрос:

У меня есть таблица со многими строками, каждая из которых принадлежит разным пользователям.

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

Я бы хотел, чтобы результат отображал среднее значение для каждой строки для пользователя, но в настоящее время он просто отображает значение для этой конкретной строки.

Пример :

 CREATE TABLE test 
(
    id INT,
    FOO INT,
    BAR INT,
    X INT,
    user_id INT
);

INSERT INTO test (id, FOO, BAR, X, user_id) VALUES (1, 1, 2, 3, 1);
INSERT INTO test (id, FOO, BAR, X, user_id) VALUES (2, 3, 2, 1, 1);
INSERT INTO test (id, FOO, BAR, X, user_id) VALUES (3, 6, 6, 6, 1);
INSERT INTO test (id, FOO, BAR, X, user_id) VALUES (4, 100, 100, 100, 1);

INSERT INTO test (id, FOO, BAR, X, user_id) VALUES (4, 3000, 3000, 3000, 2);
INSERT INTO test (id, FOO, BAR, X, user_id) VALUES (4, 400, 400, 400, 2);
  

И запрос, который я пытаюсь запустить:

 SELECT 
    id, foo, bar, x,
    AVG(foo) AS "avg_foo",
    AVG(bar) AS "avg_bar",
    AVG(x) AS "avg_x"
FROM 
    test
WHERE
    user_id = 1
GROUP BY
    id, foo, bar, x;
  

Но я просто получаю то же число, которое находится в столбце «foo» для строки, что и результат для avg_foo, где вместо этого я пытаюсь получить среднее значение всех строк «foo», отображаемых в каждой строке (каждый раз это будет одно и то же число).

В настоящее время я получаю этот результат :

 | id  | foo | bar | x   | avg_foo                | avg_bar              | avg_x                  |
| --- | --- | --- | --- | ---------------------- | -------------------- | ---------------------- |
| 1   | 1   | 2   | 3   | 1.00000000000000000000 | 2.0000000000000000   | 3.0000000000000000     |
| 2   | 3   | 2   | 1   | 3.0000000000000000     | 2.0000000000000000   | 1.00000000000000000000 |
| 3   | 6   | 6   | 6   | 6.0000000000000000     | 6.0000000000000000   | 6.0000000000000000     |
| 4   | 100 | 100 | 100 | 100.0000000000000000   | 100.0000000000000000 | 100.0000000000000000   |
  

В то время как я ожидаю:

 | id  | foo | bar | x   | avg_foo                | avg_bar              | avg_x                  |
| --- | --- | --- | --- | ---------------------- | -------------------- | ---------------------- |
| 1   | 1   | 2   | 3   | 27.5      | 27.5    | 27.5      |
| 2   | 3   | 2   | 1   | 27.5      | 27.5    | 27.5      |
| 3   | 6   | 6   | 6   | 27.5      | 27.5    | 27.5      |
| 4   | 100 | 100 | 100 | 27.5      | 27.5    | 27.5      |

---
  

Я сделал скрипку

https://www.db-fiddle.com/f/5tGQkqEnqB8et75eJ7NNWt/0

Ответ №1:

Используйте оконные функции:

 SELECT id, foo, bar, x,
       avg(foo) over (partition by user_id) as "avg_foo",
       avg(bar) over (partition by user_id) as "avg_bar",
       avg(x) over (partition by user_id) as "avg_x"
FROM test
WHERE user_id = 1
  

Комментарии:

1. Спасибо. Знаете ли вы, почему результат возвращается в виде строки, а не int / float ?

2. @ avg() manueser … не удается вернуть строковое значение. Что-то еще может заставить вас думать, что это строка.

Ответ №2:

Кажется, вам нужны оконные функции:

 select t.*,
    avg(foo) over() as avg_foo,
    avg(bar) over() as avg_bar,
    avg(x)   over() as avg_x
from test t
where user_id = 1