在 window 的最大时间段内聚合(SUM 或 AVG)

Aggregate(SUM or AVG) over a window for max time period

我有一个 INPUT table,如下所示。我想计算 2 个指标:

sellthru = sum(qty sold over 4 weeks) / initial stock at the beginning of the 4-week period x 100

coverage = stock at the end of the 4-week period / AVG(qty sold over 4 weeks) / x 100

输出 table 应该只显示给定条形码的最大周数。

我试过使用 window 函数执行此操作,但只能设法获得滚动值。

sum(quantity) over (partition by barcode order by SUM(week_number) ROWS BETWEEN 4 PRECEDING AND 1 PRECEDING)/NULLIF(sum(stock),0)::decimal

--Code to create the table: 
CREATE TABLE sell ( week_number int, barcode int, quantity int, stock int );

-- Insert values    
insert into sell values (1,222,3,2); insert into sell values (2,222,1,0); insert into sell values (3,222,2,3); insert into sell values (4,222,2,1); insert into sell values (5,222,2,2); insert into sell values (6,222,2,2); insert into sell values (1,333,4,10); insert into sell values (2,333,5,2); insert into sell values (3,333,1,3); insert into sell values (4,333,0,4);

根据条件使用子查询和不同的 window 函数/分析函数以获得预期输出。

-- PostgreSQL(v11)
SELECT tt.week_number, tt.barcode
     , ((tt.total_qty :: decimal(10, 2) / tt.min_stock :: decimal(10, 2)) * 100) :: decimal(10, 2) sellthru
     , ((tt.max_stock :: decimal(10, 2) / tt.avg_qty :: decimal(10, 2)) * 100) :: decimal(10, 2) coverage
FROM (SELECT t.week_number, t.barcode, t.row_num
           , SUM(quantity) OVER (PARTITION BY t.barcode ORDER BY t.row_num rows between current row and unbounded following) total_qty
           , AVG(quantity) OVER (PARTITION BY t.barcode ORDER BY t.row_num rows between current row and unbounded following) avg_qty
           , FIRST_VALUE(stock) OVER (PARTITION BY t.barcode ORDER BY t.row_num rows between current row and unbounded following) max_stock
           , LAST_VALUE(stock) OVER (PARTITION BY t.barcode ORDER BY t.row_num rows between current row and unbounded following) min_stock
      FROM (SELECT *
                 , ROW_NUMBER() OVER (PARTITION BY barcode ORDER BY week_number DESC) row_num
            FROM sell) t
      WHERE t.row_num <= 4) tt
WHERE tt.row_num = 1

使用多个 CTE 的另一种方法

-- PostgreSQL (v11)
WITH cte AS (
     SELECT *
          , ROW_NUMBER() OVER (PARTITION BY barcode ORDER BY week_number DESC) row_num
     FROM sell
), cte_1 AS (
      SELECT week_number, barcode, row_num
           , SUM(quantity) OVER (PARTITION BY barcode ORDER BY row_num rows between current row and unbounded following) total_qty
           , AVG(quantity) OVER (PARTITION BY barcode ORDER BY row_num rows between current row and unbounded following) avg_qty
           , FIRST_VALUE(stock) OVER (PARTITION BY barcode ORDER BY row_num rows between current row and unbounded following) max_stock
           , LAST_VALUE(stock) OVER (PARTITION BY barcode ORDER BY row_num rows between current row and unbounded following) min_stock
      FROM cte
      WHERE row_num <= 4
)
SELECT week_number, barcode
     , ((total_qty :: decimal(10, 2) / min_stock :: decimal(10, 2)) * 100) :: decimal(10, 2) sellthru
     , ((max_stock :: decimal(10, 2) / avg_qty :: decimal(10, 2)) * 100) :: decimal(10, 2) coverage
FROM cte_1
WHERE row_num = 1

请检查url https://dbfiddle.uk/?rdbms=postgres_11&fiddle=5aa32e2e8f80be713774fc3ee761c2be

N.B.: 如果将来考虑 week_number = 5 或任何数字,则使用 row_num <= 5 或该数字代替共 row_num <= 4