Postgres的股票计算

时间:2015-01-12 02:15:59

标签: sql postgresql window-functions running-total

我在Postgres中有一个表p1,其中包含以下内容:

| id | product_id | transaction_date | quantity |
|----|------------|------------------|----------|
| 1  | 1          | 2015-01-01       | 1        |
| 2  | 1          | 2015-01-02       | 2        |
| 3  | 1          | 2015-01-03       | 3        |

p2表包含以下产品:

| id | product      | stock |
|----|--------------|-------|
| 1  | Product A    | 15    |
对于stock中的每个新记录,p2'中的p1已被缩减。

如何重建以前的状态以获得此结果?

| product   | first_stock | quantity | last_stock |
|-----------|-------------|----------|------------|
| Product A | 21          | 1        | 20         |
| Product A | 20          | 2        | 18         |
| Product A | 18          | 3        | 15         |

我尝试使用lead()获取当前行之后的数量。

SELECT p2.product, p1.quantity, lead(p1.quantity) OVER(ORDER BY p1.id DESC)
FROM p1 INNER JOIN p2 ON p1.product_id = p2.id;

但是如何计算当前股票的前导行?

1 个答案:

答案 0 :(得分:3)

您不需要lead(),您需要在所有行之间运行总和,以便从交易数据中重建以前的状态:

SELECT p2.product
     , p2.stock + px.sum_quantity            AS first_stock
     , px.quantity
     , p2.stock + px.sum_quantity - quantity AS last_stock
FROM   p2
JOIN (
   SELECT product_id, quantity, transaction_date
        , sum(quantity) OVER (PARTITION BY product_id
                              ORDER BY transaction_date DESC) AS sum_quantity
   FROM   p1
   ) px ON px.product_id = p2.id
ORDER  BY px.transaction_date;

假设事件过程实际由transaction_date表示。

使用聚合函数sum()作为窗口聚合函数来获取运行总和。使用子查询,因为我们多次使用数量的运行总和(sum_quantity) 对于last_stock减去当前行的quantity(在冗余添加之后)。

挑剔

理论上,使用窗口框架的自定义框架定义仅对前面行的数量求和会更便宜,因此我们不要添加和减去<的数量< em>当前行冗余。但这实际上更复杂,而且速度更快:

SELECT p2.id, p2.product, px.transaction_date  -- plus id and date for context
     , p2.stock + COALESCE(px.pre_sum_q + px.quantity, 0) AS first_stock
     , px.quantity
     , p2.stock + COALESCE(px.pre_sum_q, 0)               AS last_stock
FROM  p2
LEFT JOIN (
   SELECT id, product_id, transaction_date
        , quantity
        , sum(quantity) OVER (PARTITION BY product_id
                              ORDER BY transaction_date DESC
               ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) AS pre_sum_q
   FROM   p1
   ) px ON px.product_id = p2.id
ORDER  BY px.transaction_date, px.id;

此相关答案中的帧定义说明:

在此过程中,还展示了如何使用LEFT JOINCOALESCE来防止错误的行和NUll值用于不具有任何相关行的产品p1,如果同一天的同一产品有多个交易,则为稳定的排序顺序。

仍然假设要定义的所有列都是NOT NULL,或者你需要为具有NULL值的极端情况做更多的事情。