Использование FIRST_VALUE без включения внутренних столбцов в группу по - PullRequest
0 голосов
/ 26 февраля 2019

Я использую таблицу, которая выглядит следующим образом:

userID, eventDate, eventName
1  2019-01-01  buySoup
2  2019-01-01  buyEggs
2  2019-01-03  buyMilk
2  2019-01-04  buyMilk
3  2019-01-02  buyBread
3  2019-01-03  buyBread

Мой текущий запрос:

SELECT
    userID,
    FIRST_VALUE(eventName) OVER (
        PARTITION BY userID ORDER BY eventDate ASC
    ) AS firstBought 
FROM table 
GROUP BY userID

Мне кажется, что это должно вернуть:

userID, firstBought
1  buySoup
2  buyEggs
3  buyBread

Вместо этого выдается ошибка:

«ОШИБКА: Столбец« table.eventName »должен появляться в предложении GROUP BY или использоваться в статистической функции»

Есть ли способ получить это значение без включения его в группу по функции или создания подзапроса?Я использую PostgreSQL.

Если я включу его в предложение group by, он вернет

userID, firstBought
1  buySoup
2  buyEggs
2  buyEggs
2  buyEggs
3  buyBread
3  buyBread

Я понимаю, что могу сделать это подзапросом, а затем группировать по userID, firstBought, но я бы не хотел создавать другой подзапрос.

Ответы [ 4 ]

0 голосов
/ 27 февраля 2019

Я согласен с А. Сондерсом.

Вам необходим внешний запрос.

За исключением SELECT DISTINCT, который фактически сводится к GROUP BY всех столбцов списка SELECT,Вы не можете смешивать функции OLAP и GROUP BY в одном и том же SELECT.

Итак, если у вас есть MAX (), вам необходимо:

WITH -- your input data ...
input(userID,eventDate,eventName) AS (
          SELECT 1,DATE '2019-01-01','buySoup'
UNION ALL SELECT 2,DATE '2019-01-01','buyEggs'
UNION ALL SELECT 2,DATE '2019-01-03','buyMilk'
UNION ALL SELECT 2,DATE '2019-01-04','buyMilk'
UNION ALL SELECT 3,DATE '2019-01-02','buyBread'
UNION ALL SELECT 3,DATE '2019-01-03','buyBread'
)
,
getfirstbought AS (
  SELECT 
    userid
  , eventdate
  , FIRST_VALUE(eventname) OVER (
      PARTITION BY userid ORDER BY eventdate
   ) AS firstbought
  FROM input
)
SELECT
  userid
, firstbought
, MAX(eventdate) AS maxdt
FROM getfirstbought
GROUP BY 1,2;
-- out  userid | firstbought |   maxdt    
-- out --------+-------------+------------
-- out       2 | buyEggs     | 2019-01-04
-- out       3 | buyBread    | 2019-01-03
-- out       1 | buySoup     | 2019-01-01
-- out (3 rows)
-- out 
-- out Time: First fetch (3 rows): 22.157 ms. All rows formatted: 22.208 ms
0 голосов
/ 26 февраля 2019

Полагаю, что PostgreSQL DISTINCT ON мог бы добиться цели:

SELECT DISTINCT ON (userid)
       userid, eventdate, eventname
FROM "table"
ORDER BY (eventdate);

Это даст вам строку за userid с минимальным значением eventdate.

0 голосов
/ 26 февраля 2019

FIRST_VALUE не является агрегатной функцией.Это аналитическая оконная функция.Таким образом, ваш базовый запрос не нуждается в предложении GROUP BY.Это должно быть переписано как:

SELECT 
        userID,
        FIRST_VALUE(eventName) over (PARTITION BY userID ORDER BY eventDate ASC) AS firstBought
FROM table;

Из одного из ваших вышеупомянутых комментариев кажется, что есть другие функции, которые вы используете, включая агрегатные функции, такие как MAX.Чтобы выполнить то, что вы пытаетесь сделать, вам нужно использовать вышеуказанный запрос в качестве подзапроса.Это позволит вам использовать агрегатные функции и получать уникальные значения из вашего базового запроса.Запрос может выглядеть примерно так (я добавил столбец цены в качестве примера).

SELECT userID, firstBought, MAX(price)
FROM (
        SELECT userID, price, FIRST_VALUE(eventName) over (partition by userID order by eventDate ASC) as firstBought 
        from test
) x
GROUP BY userId, firstBought;

Это должно сработать!Вы можете использовать другие агрегатные функции для внешнего запроса и дополнительные оконные функции в подзапросе.

0 голосов
/ 26 февраля 2019

Вместо group by, используйте select distinct:

select distinct userID,
       FIRST_VALUE(eventName) over (partition by userID order by eventDate ASC) as firstBought 
from table ;

Или вы можете использовать массивы:

select userId,
       (array_agg(eventName order by eventDate))[1] as firstBought
from table
group by userId;

У Postgres нет «первой» функции агрегирования, но это работает довольно хорошо.

...