Как создать агрегат для целого числа с сгруппированным столбцом, для которого я хочу включить только некоторые? - PullRequest
0 голосов
/ 17 апреля 2020

У меня есть таблица prices, содержащая все цены, которые были у некоторых продуктов:

CREATE TABLE prices (
  id INT,
  product_id INT, /*Foreign key*/
  created_at TIMESTAMP,
  price INT
);

Первая сущность для product_id - это начальная цена продажи. Если продукт будет уменьшен, будет добавлен новый объект.

Я хотел бы найти среднее и полное изменение цены за день по всем продуктам .

Это некоторые примеры данных:

INSERT INTO prices (id, product_id, created_at, price) VALUES (1, 1, '2020-01-01', 11000);
INSERT INTO prices (id, product_id, created_at, price) VALUES (2, 2, '2020-01-01', 3999);
INSERT INTO prices (id, product_id, created_at, price) VALUES (3, 3, '2020-01-01', 9999);
INSERT INTO prices (id, product_id, created_at, price) VALUES (4, 4, '2020-01-01', 2000);
INSERT INTO prices (id, product_id, created_at, price) VALUES (5, 1, '2020-01-02', 9999);
INSERT INTO prices (id, product_id, created_at, price) VALUES (6, 2, '2020-01-02', 2999);    
INSERT INTO prices (id, product_id, created_at, price) VALUES (7, 5, '2020-01-02', 2999);
INSERT INTO prices (id, product_id, created_at, price) VALUES (8, 1, '2020-01-03', 8999);
INSERT INTO prices (id, product_id, created_at, price) VALUES (9, 1, '2020-01-03 10:00:00', 7000);
INSERT INTO prices (id, product_id, created_at, price) VALUES (10, 5, '2020-01-03', 4000);
INSERT INTO prices (id, product_id, created_at, price) VALUES (11, 6, '2020-01-03', 3999);
INSERT INTO prices (id, product_id, created_at, price) VALUES (12, 3, '2020-01-03', 6999);

Ожидаемый результат должен быть:

date       mean_price_change    total_price_change
2020-01-01 0                    0
2020-01-02 1000.5               2001
2020-01-03 1666                 4998

Объяснение:

  • Среднее снижение цены и итоговое значение по '2020-01-01' было 0, поскольку все продукты были новыми на эту дату.
  • Однако в '2020-01-02' среднее изменение цены было: (11000-9999 + 3999-2999) / 2 = 1000,5, поскольку product_id 1 и 2 были уменьшены до 9999 и 2999 в тот день, и их предыдущие цены были 11000 и 3999, и там общее снижение будет: (11000-9999 + 3999-2999) = 2001.
  • Только для '2020-01-03' product_id 1, 3 и 5 были изменены. 1 в два разных времени дня: 9999 => 8999 => 7000 (последний управляющий) и 3: с 9999 => 6999 a затем 5: с 2999 => 4000. Это дает всего: (9999-7000 + 9999-6999 + 2999-4000) = 4998 и снижение средней цены в этот день: 1666

Я также добавил сюда данные: https://www.db-fiddle.com/f/tJgoKFMJxcyg5gLDZMEP77/1

Я сказал поиграть с некоторыми DISTINCT ON, но, похоже, это не так ...

Ответы [ 3 ]

2 голосов
/ 18 апреля 2020

Вы, похоже, хотите lag() и агрегацию:

select created_at, avg(prev_price - price), sum(prev_price - price)
from (select p.*, lag(price) over (partition by product_id order by created_at) as prev_price
      from prices p
     ) p
group by created_at
order by created_at;

У вас есть две цены на продукт 1 на 2020-01-03. Как только я это исправлю, я получу те же результаты, что и в вашем вопросе. Здесь - это дБ <> скрипка.

РЕДАКТИРОВАТЬ:

Для обработки нескольких цен в день:

select created_at, avg(prev_price - price), sum(prev_price - price)
from (select p.*, lag(price) over (partition by product_id order by created_at) as prev_price
      from (select distinct on (product_id, created_at::date) p.*
            from prices p
            order by product_id, created_at::date
           ) p
     ) p
group by created_at
order by created_at;
0 голосов
/ 17 апреля 2020

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

  • их продукт совпадает
  • раньше действительно раньше, чем текущий
  • ранее - это самый последний элемент на дату раньше, чем текущий
  • текущий - это самый последний элемент на собственную дату

select today.product_id, (today.price - coalesce(earlier.price)), today.created_at as difference from prices current join prices earlier on today.product_id = earlier.product_id and earlier.created_at < current.created_at where not exists ( select 1 from prices later where later.product_id = today.product_id and ( ((today.created_at = later.created_at) and (today.id < later.id)) or ((earlier.created_at <= later.created_at) and (earlier.id < later.id)) ) );

Теперь давайте сделаем некоторая агрегация:

select created_at, avg(today.price - coalesce(earlier.price)) as mean, sum(today.price - coalesce(earlier.price)) as total
from prices current
left join prices earlier
on today.product_id = earlier.product_id and earlier.created_at < current.created_at
where not exists (
    select 1
    from prices later
    where later.product_id = today.product_id and
    (
     ((today.created_at = later.created_at) and (today.id < later.id)) or
     ((earlier.created_at <= later.created_at) and (earlier.id < later.id))
    )
)
group by created_at
order by created_at;
0 голосов
/ 17 апреля 2020

попробуйте

select 
created_at, 
avg(change),
sum(change)
from
(
    with cte as 
    (
    select 
    id, 
    product_id,
    created_at,
    lag(created_at) over(order by product_id, created_at) as last_date,
    price
    from prices
    )
    select
    c.id,
    c.product_id,
    c.created_at,
    c.last_date,
    p.price as last_price,
    c.price,
    COALESCE(p.price - c.price,0) as change
    from cte c
    left join prices p on c.product_id =p.product_id  and c.last_date =p.created_at
  where p.price != c.price or p.price is null
) tmp
group by created_at
order by created_at
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...