Выполнение «различного» для всех уникальных порогов в таблице postgres - PullRequest
1 голос
/ 07 ноября 2019

У меня есть таблица Postgres 11 с именем sample_a, которая выглядит следующим образом:

 time | cat | val
------+-----+-----
    1 |   1 |   5
    1 |   2 |   4
    2 |   1 |   6
    3 |   1 |   9
    4 |   3 |   2

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

Я считаю, что у меня есть запрос, чтобы сделать это для заданного временного шага. Например, для времени 3 я могу выполнить следующий запрос:

select sum(val)::numeric / count(val) as result from (
    select distinct on (cat) * from sample_a where time <= 3  order by cat, time desc
) x;

и получить 6.5. (Это потому, что во время 3 самое последнее из категории 1 равно 9, а самое последнее из категории 2 равно 4. Количество значений равно 2, и они суммируются до 13 и 13 / 2 - это 6.5.)

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

 time | result
------+----------
    1 |   4.5
    2 |   5
    3 |   6.5
    4 |   5

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

Как будет выглядеть этот новый запрос?

Ответы [ 2 ]

1 голос
/ 07 ноября 2019

Посмотрите, приемлема ли производительность таким образом. Синтаксис может потребовать незначительных изменений:

select t.time, avg(mr.val) as result
from (select distinct time from sample_a) t,
    lateral (
        select distinct on (cat) val
        from sample_a a
        where a.time <= t.time
        order by a.cat, a.time desc
    ) mr
group by t.time
0 голосов
/ 07 ноября 2019

Я думаю, вам просто нужны кумулятивные функции:

select time,
       sum(sum(val)) over (order by time) / sum(sum(num_val)) over (order by time) as result
from (select time, sum(val) as sum_val, count(*) as num_val
      from sample_a a
      group by time
     ) a;

Обратите внимание, что если val является целым числом, вам может потребоваться преобразовать в числовое значение, чтобы получить дробные значения.

Это можеттакже можно выразить без подзапроса:

select time,
       sum(sum(val)) over (order by time) / sum(count(*)) over (order by time) as result
from sample_a
group by time
...