Aggregation function to get the difference or rati

2019-07-28 07:40发布

问题:

I have a table full of prices, items, and dates. An example of this is:

AA, 1/2/3024, 1.22
AA, 1/3/3024, 1.23
BB, 1/2/3024, 4.22
BB, 1/3/3024, 4.23

Within the data there are only two rows per price, and they are ordered by date. How would I condense this data set into a single product row showing the difference from the last price to the previous? [Also this applies to a ratio, so AA would produce 1.23/1.22].

The result should look like

AA, todays price-yesterdays price

Despite being a sum function, there is no subtraction function over a list.

I'm using postgres 9.1.

回答1:

select product,
       sales_date,
       current_price - prev_price as diff
from (
  select product,
         sales_date, 
         price as current_price,
         lag(price) over (partition by product order by sales_date) as prev_price,
         row_number() over (partition by product order by sales_date desc) as rn
  from the_unknown_table
) t
where rn = 1;

SQLFiddle example: http://sqlfiddle.com/#!15/9f7d6/1



回答2:

Since there are only two rows per price, this can be much simpler and faster:

SELECT n.item, n.price - o.price AS diff, n.price / o.price AS ratio
FROM   price n                 -- "new"
JOIN   price o USING (item)    -- "old"
WHERE  n.day > o.day;

->SQLfiddle

This form carries the additional benefit, that you can use all columns from both rows directly.


For more complex scenarios (not necessary for this), you could use window functions as has been pointed out. Here is a simpler approach than what has been suggested:

SELECT DISTINCT ON (item)
       item
      ,price - lead(price) OVER (PARTITION BY item ORDER BY day DESC) AS diff
FROM   price
ORDER  BY item, day DESC;

Only one window function is needed here. And one query level, since DISTINCT ON is applied after window functions. The sort order in the window agrees with overall sort order, helping performance.



回答3:

If there's only two rows per item, then

SELECT item, MAX(price) - MIN(price) AS diff, MAX(price) / MIN(price) AS ratio
FROM yourtable
GROUP BY item

will do the trick.