Вычислить текущую сумму в оконной функции

У меня есть проблемы с этой текущей суммой в Redshift (использует Postgres 8):

select extract(month from registration_time) as month
 , extract(week from registration_time)%4+1 as week
 , extract(day from registration_time) as day
 , count(*) as count_of_users_registered
 , sum(count(*)) over (ORDER BY (1,2,3))
from loyalty.v_user
group by 1,2,3
order by 1,2,3
;

Ошибка, которую я получаю:

ERROR: 42601: Aggregate window functions with an ORDER BY clause require a frame clause

person simplycoding    schedule 06.11.2015    source источник
comment
Вы не можете использовать номера столбцов для порядка в определении окна (и (1,2,3) отличается от 1,2,3 - не используйте бесполезные круглые скобки). over (order by registration_time) должно делать то, что вы хотите   -  person a_horse_with_no_name    schedule 06.11.2015
comment
Все еще получаю ту же ошибку при использовании order by registration_time. Правилен ли мой синтаксис для самой функции sum?   -  person simplycoding    schedule 06.11.2015


Ответы (1)


Вы можете запускать оконные функции для результата агрегатной функции на том же уровне запроса. В этом случае гораздо проще использовать подзапрос:

SELECT *, sum(count_registered_users) OVER (ORDER BY month, week, day) AS running_sum
FROM  (
   SELECT extract(month FROM registration_time)::int     AS month
        , extract(week  FROM registration_time)::int%4+1 AS week
        , extract(day   FROM registration_time)::int     AS day
        , count(*) AS count_registered_users
   FROM   loyalty.v_user
   GROUP  BY 1, 2, 3
   ORDER  BY 1, 2, 3
   ) sub;

Я также исправил синтаксис для вычисления выражений week. extract() возвращает double precision, но оператор по модулю % не принимает числа double precision. Я применил все три к integer, пока работал над этим.

Например, @a_horse прокомментировал, вы не можете использовать позиционные ссылки в предложении ORDER BY оконной функции (в отличие от предложения ORDER BY запроса).

Однако вы не можете использовать over (order by registration_time) в этом запросе, так как вы группируете по month, week, day. registration_time не агрегируется и не содержится в предложении GROUP BY, как это требовалось бы. На этом этапе оценки запроса вы больше не можете получить доступ к столбцу.

Вы можете повторить выражения первых трех элементов SELECT в предложении ORDER BY, чтобы заставить его работать:

SELECT extract(month FROM registration_time)::int     AS month
     , extract(week  FROM registration_time)::int%4+1 AS week
     , extract(day   FROM registration_time)::int     AS day
     , count(*) AS count_registered_users
     , sum(count(*)) OVER (ORDER BY 
              extract(month FROM registration_time)::int
            , extract(week  FROM registration_time)::int%4+1
            , extract(day   FROM registration_time)::int) AS running_sum
FROM   loyalty.v_user
GROUP  BY 1, 2, 3
ORDER  BY 1, 2, 3;

Но это кажется довольно шумным. (Хотя производительность была бы хорошей.)

В сторону: меня интересует цель week%4+1 ... Весь запрос может быть проще.

Связанный:

person Erwin Brandstetter    schedule 08.11.2015
comment
Да, ваш ответ и предложение имеют смысл. Я получал ту же ошибку, пока не попытался включить rows unbounded preceding после предложения order by month, week, day, и это сработало. Так что для тех, кто столкнется с этим в будущем, похоже, что rows unbounded preceding или любой другой вариант, который вы хотите, требуется. Опять же, в документации AWS нет ясности по этому поводу, так как это на Redshift. - person simplycoding; 09.11.2015
comment
@simplycoding: Redshift - это то, с чего вы должны упомянуть в вопросе для начала, поскольку это не Postgres. Произведено от него, но существенно отличается. Приведенный выше код работает в Postgres как есть, где цитировать, The default framing option is RANGE UNBOUNDED PRECEDING. - person Erwin Brandstetter; 09.11.2015